Troubleshooting
Structured Error Format
When a request fails, the server responds with a frame whose payload contains a structured error:
{
"header": {
"id": "msg_2",
"resource": "session",
"method": "join",
"kind": "response",
"replyTo": "msg_1"
},
"payload": {
"status": "error",
"error": {
"code": "session.not_found",
"message": "Session does not exist.",
"resource": "session",
"retry": false,
"details": null
}
}
}The SDK surfaces these as StarfishError instances with code, message, resource, retry, and details fields. The retry field indicates whether the client should retry the operation.
Error Codes
Connection Errors
CONNECTION_FAILED
The WebSocket connection could not be established.
Causes:
- Server is not running
- Wrong server URL
- Firewall or proxy blocking WebSocket connections
Fix: Verify the server is running and the URL is correct. Check that your network allows WebSocket connections (some corporate proxies block them).
NO_WEBSOCKET
No WebSocket implementation is available.
Causes:
- Running in Node.js without providing a WebSocket factory
Fix: Install ws and pass it via the webSocketFactory option:
import WebSocket from "ws";
const client = new StarfishClient({
server: "ws://localhost:4000",
webSocketFactory: (url) => new WebSocket(url),
});NOT_CONNECTED
Attempted to send a frame while not connected.
Causes:
- Calling
publish,send,subscribe, or other methods beforeconnect()completes - Connection dropped and reconnection hasn't succeeded yet
Fix: Ensure await client.connect() has resolved before calling other methods. Monitor connectionState$ to know when you're connected.
CONNECTION_LOST
The WebSocket connection closed unexpectedly.
Causes:
- Network interruption
- Server shutdown or restart
- Idle timeout
Fix: The client will automatically attempt to reconnect if reconnect.enabled is true (the default). Pending requests will be rejected with this error — retry them after reconnection.
DISCONNECTED
The client was intentionally disconnected.
Causes:
disconnect()was called while requests were pending
Fix: This is expected behavior. Complete or cancel pending operations before disconnecting.
Session Errors
NO_SESSION
Attempted an operation that requires a session without joining one first.
Causes:
- Calling
publish,subscribe,presence.set,save, orsendbeforejoin()
Fix: Call await client.join("session-name") before using session features.
Project & Matchmaking Errors
These arise from the Project model: credential/Project binding, the lockdown flag, and declared matchmaking policies.
project.forbidden
The credential is valid but binds to a different Project than the one the client declared.
Causes:
- Your credential maps to Project
A(via itskeys) but you declaredproject: "B" - You requested a Project the authenticator does not grant
Fix: Declare the Project your credential grants, or omit project and let the authenticator assign it. A credential keys binding is authoritative — see Projects and authentication.
project.not_found
A referenced Project was not found where a declared one is required.
Fix: Provision the Project through the control plane, or reference an existing one. Unprovisioned Projects are otherwise implicit and permissive.
project.lockdown
Ad-hoc creation (create: true on a session or pool) was attempted in a locked-down Project.
Causes:
- The Project was provisioned with
lockdown: true, which permits only declared policies
Fix: Enter a declared policy by name instead of creating a pool/session ad-hoc, or clear lockdown on the Project config.
pool.policy_not_found
The referenced matchmaking policy is not declared on the connection's Project.
Causes:
- Typo in the policy name
- The Project is implicit (has no declared policies)
Fix: Reference a policy that exists on the Project (see its config), or provision one.
pool.policy_required
No policy was named and the Project declares no default policy.
Fix: Name a policy explicitly — joinProject(project, { policy }) — or declare a default: true policy on the Project.
The runtime pool errors (
pool.not_found,pool.mode_mismatch, and the otherpool.*codes) are documented in the Pool Matchmaking API reference.
Data Errors
PAYLOAD_TOO_LARGE
A payload exceeds the size limit.
Causes:
- Presence data exceeds 8 KB
- Data value exceeds 256 KB
- WebSocket message exceeds 64 KB
Fix: Reduce the payload size. For large data, store it externally and share a reference. See Best Practices for all limits.
TOPIC_NAME_TOO_LONG
A topic name exceeds 128 characters.
Fix: Use shorter topic names. Consider using a hierarchical naming scheme like "drawing/layer1" instead of encoding data in the topic name.
WebRTC Errors
RTC_NOT_ENABLED
Attempted an RTC operation without providing RTC options.
Fix: Pass rtc options when creating the client:
const client = new StarfishClient({
server: "ws://localhost:4000",
rtc: {
peerConnectionFactory: (config) => new RTCPeerConnection(config),
},
});TRANSPORT_UNAVAILABLE
RTC transport was requested with fallback: false but no RTC peers are connected.
Fix: Either set fallback: true (the default) to fall back to WebSocket, or ensure RTC peer connections are established before sending.
Common Issues
Messages Not Arriving
Check subscription order: Set up your
topic$listener before callingsubscribe()to avoid missing messages that arrive between the subscription and listener setup.Check session membership: Both sender and receiver must be in the same session.
Check topic name: Topic names are case-sensitive.
"Cursor"and"cursor"are different topics.Check
includeSelf: By default, you don't receive your own published messages. Setdelivery.includeSelf: trueif you need them.
Connection Keeps Dropping
Check heartbeat: If the server doesn't receive heartbeats within the expected interval, it may close the connection. Ensure no long-running synchronous code blocks the event loop.
Check proxy/load balancer timeouts: Some proxies close idle WebSocket connections. The heartbeat should prevent this, but verify your proxy's timeout settings.
Check reconnection settings: If
maxRetriesis too low, the client may give up reconnecting. The default isInfinity.
High Latency
Use unreliable delivery for high-frequency data like cursor positions. Reliable delivery through the server adds round-trip latency.
Enable WebRTC for peer-to-peer communication. This bypasses the server for data delivery.
Sync clocks with
client.clock.sync()before using time-sensitive features. Without sync,client.clock.runAt()may fire at the wrong time.
Node.js Specific
Process doesn't exit after disconnect:
The WebSocket connection may keep the Node.js event loop alive. Call disconnect() and ensure all listeners are cleaned up:
await client.disconnect();
process.exit(0);ws package version:
Use ws version 8.x or later. Older versions may not support all features used by the SDK.