toeverything/AFFiNE · error · BadRequest
bad_request
bad_request
Error message
Invalid space join batch payload.
What it means
The space sync WebSocket gateway parses every space-join-batch message with parseJoinSpaceBatchMessage (gateway.ts:254). This variant is the very first check: the incoming message failed isRecord, meaning it is not a plain object (it is a string, number, boolean, array, or null). All later validation (spaces array, clientVersion, entry shape) only runs after this passes.
Solutions
- Send a plain object with the required shape: { spaces: [...], clientVersion: '<version string>' }
- Make sure the payload is stringified exactly once — check for JSON.stringify on both the emit side and the transport wrapper
- Upgrade the client to the version whose sync protocol matches this gateway
Example fix
// before: raw/double-encoded payload
socket.emit(joinSpaceBatch, JSON.stringify({ spaces }));
// after: plain object with required fields
socket.emit(joinSpaceBatch, { spaces: entries, clientVersion: appVersion }); Defensive patterns
Strategy: validation
Validate before calling
// validate before emitting over the socket const msg = buildJoinSpaceBatch(pending, appVersion); assert(typeof msg === 'object' && msg !== null && !Array.isArray(msg)); socket.emit(joinSpaceBatch, msg);
Type guard
const isRecord = (v: unknown): v is Record<string, unknown> => typeof v === 'object' && v !== null; const isJoinSpaceBatchMessage = (m: unknown): m is JoinSpaceBatchMessage => isRecord(m) && Array.isArray(m.spaces) && m.spaces.length > 0 && typeof m.clientVersion === 'string' && m.clientVersion.length > 0;
Try / catch
// bad_request from the gateway is a client bug: log and stop, never retry
try {
await socket.emitWithAck(joinSpaceBatch, msg);
} catch (e) {
if (e instanceof BadRequest) { // code === 'bad_request'
console.error('malformed join batch', e.message);
return; // fix the payload, do not resend it unchanged
}
throw e;
} Prevention
- Type all outbound WS messages with the gateway's own interfaces (JoinSpaceBatchMessage etc.)
- Stringify the payload exactly once; assert isRecord on the value handed to emit
- Unit-test the serializer against the gateway's parse functions when possible
When it happens
Trigger: A client emits the space-join-batch message with a non-object payload: a JSON string that was never parsed, a bare scalar, an array, or null/undefined.
Common situations: Hand-rolled WebSocket clients or scripts; accidentally double-encoding the payload (JSON.stringify applied twice, or stringify on a side that also serializes); an older client version sending a different message shape for this event.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
AI-assisted analysis of toeverything/AFFiNE@b6de0ad51b (2026-08-21).
Data as JSON: /api/errors/da64cc79d5aac58b.
Report an issue: GitHub.
Appendix: source
Thrown at packages/backend/server/src/core/sync/gateway.ts:254
'sync.awareness.collect': SyncAwarenessEvent;
'sync.awareness.updated': SyncAwarenessEvent & {
awarenessUpdate: string;
};
'sync.permissions.changed': {
spaceType: SpaceType;
spaceId: string;
docId?: string;
};
}
}
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === 'object' && value !== null;
}
function parseJoinSpaceBatchMessage(message: unknown): JoinSpaceBatchMessage {
if (!isRecord(message)) {
throw new BadRequest('Invalid space join batch payload.');
}
const { spaces, clientVersion } = message;
if (!Array.isArray(spaces) || spaces.length === 0) {
throw new BadRequest('Space join batch must not be empty.');
}
if (spaces.length > MAX_SPACE_JOIN_BATCH_SIZE) {
throw new BadRequest(
`Space join batch exceeds limit (${MAX_SPACE_JOIN_BATCH_SIZE}).`
);
}
if (typeof clientVersion !== 'string' || clientVersion.length === 0) {
throw new BadRequest('Space join batch requires a client version.');
}
const entries = spaces.map((space, index) => {
if (!isRecord(space)) {
throw new BadRequest(`Invalid space join batch entry at index ${index}.`);View on GitHub (pinned to b6de0ad51b)