clockworklabs/SpacetimeDB · error · RangeError

v3 websocket payloads must contain at least one message

Error message

v3 websocket payloads must contain at least one message

What it means

SpacetimeDB's v3 WebSocket protocol batches one or more BSATN-encoded server messages into each binary frame. forEachServerMessageV3 refuses to process a frame whose payload is empty (reader.remaining === 0) because a v3 frame must contain at least one message; an empty buffer indicates malformed framing, not a legitimate (empty) batch.

Source

Thrown at crates/bindings-typescript/src/sdk/websocket_v3_frames.ts:117

    ClientMessage.deserialize(reader)
  );
}

export function encodeServerMessagesV3(
  writer: BinaryWriter,
  messages: readonly Uint8Array<ArrayBuffer>[]
): Uint8Array<ArrayBuffer> {
  return concatenateMessagesV3(writer, messages);
}

export function forEachServerMessageV3(
  reader: BinaryReader,
  data: Uint8Array,
  visit: (message: ServerMessage) => void
): number {
  reader.reset(data);
  if (reader.remaining === 0) {
    throw new RangeError(EMPTY_V3_PAYLOAD_ERR);
  }

  let count = 0;
  while (reader.remaining > 0) {
    visit(ServerMessage.deserialize(reader));
    count += 1;
  }
  return count;
}

View on GitHub (pinned to 524b4487d9)

Solutions

  1. Align the client SDK and spacetimedb server versions so both implement the same v3 framing
  2. If you pump frames yourself (custom WebSocketFactory or tests), skip zero-length frames instead of passing them to forEachServerMessageV3
  3. Capture raw frames (browser devtools or a logging WebSocketFactory) to identify which side produces the empty payload
  4. If an unmodified server sends empty frames against a matching SDK version, file a bug with the frame capture and host version
Defensive patterns

Strategy: validation

Validate before calling

function hasV3Messages(frame: Uint8Array): boolean {
  return frame.byteLength > 0;
}
// in a custom transport/test pump:
if (!hasV3Messages(frame)) return; // skip empty frame instead of throwing

Try / catch

In custom transports, wrap forEachServerMessageV3 in try/catch for RangeError, log the frame length, and drop the malformed frame rather than killing the connection.

Prevention

When it happens

Trigger: The server (or a custom WebSocketFactory/transport feeding the SDK) delivers a zero-length data frame to the v3 message pump; also reachable when calling forEachServerMessageV3 directly with an empty Uint8Array, e.g. in hand-written tests or a custom transport loop.

Common situations: Version skew where a newer host changes framing or emits keepalive/empty frames the SDK doesn't expect; unit tests constructing frames by hand; middleware that drops or splits frame payloads.

Related errors


AI-assisted analysis of clockworklabs/SpacetimeDB@524b4487d9 (2026-08-16). Data as JSON: /api/errors/72ac606012737284. Report an issue: GitHub.