clockworklabs/SpacetimeDB · error · Error
Invalid JSON: failed to parse string
Error message
Invalid JSON: failed to parse string
What it means
parseJsonObject runs JSON.parse and converts any throw into 'Invalid JSON: failed to parse string'. In the module runtime it parses the JWT payload of an authenticated request (JwtClaimsImpl, runtime.ts:133) and is exported for handlers parsing request bodies. Any syntactically invalid JSON (trailing commas, single quotes, raw newlines, truncation, an HTML error page) triggers it.
Source
Thrown at crates/bindings-typescript/src/server/runtime.ts:110
function responseIntoWire(response: SyncResponse): [HttpResponse, Uint8Array] {
return [
{
headers: serializeHeaders(response.headers),
version: response.version,
code: response.status,
},
response.bytes(),
];
}
export function parseJsonObject(json: string): JsonObject {
let value: unknown;
try {
value = JSON.parse(json);
} catch {
throw new Error('Invalid JSON: failed to parse string');
}
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
throw new Error('Expected a JSON object at the top level');
}
// The runtime check above guarantees this cast is safe
return value as JsonObject;
}
class JwtClaimsImpl implements JwtClaims {
readonly fullPayload: JsonObject;
private readonly _identity: Identity;
/**
* Creates a new JwtClaims instance.
* @param rawPayload The JWT payload as a raw JSON string.
* @param identity The identity for this JWT. We are only taking this because we don't have a blake3 implementation (which we need to compute it).
*/View on GitHub (pinned to 524b4487d9)
Solutions
- Wrap the parse in try/catch and return a 400 with a clear message instead of letting the module throw
- Log or curl the exact raw bytes being parsed to spot truncation, BOMs, or wrong encoding
- If the input is a JWT, use a real three-part token from your identity provider rather than a placeholder string
Example fix
// before
const claims = parseJsonObject(bodyText); // throws on bad input
// after
let claims;
try {
claims = parseJsonObject(bodyText);
} catch {
return new Response(JSON.stringify({ error: 'invalid JSON body' }), { status: 400 });
} Defensive patterns
Strategy: try-catch
Validate before calling
function tryParseJson(text: string): { ok: true; value: unknown } | { ok: false } {
try {
return { ok: true, value: JSON.parse(text) };
} catch {
return { ok: false };
}
}
const parsed = tryParseJson(bodyText);
if (!parsed.ok) return new Response('invalid JSON', { status: 400 }); Try / catch
catch (e) { if (e instanceof Error && e.message === 'Invalid JSON: failed to parse string') return 400; throw e; } Prevention
- Never feed untrusted request bodies or token payloads to parseJsonObject without a try/catch
- Validate Content-Type and reject non-JSON payloads at the handler boundary
- Log raw payloads (length + first bytes) when debugging to catch truncation and HTML error pages
When it happens
Trigger: A request JWT whose payload base64url segment does not decode to valid JSON; an HTTP handler calling parseJsonObject on a request body that is not strict JSON; a body truncated or replaced by a proxy's HTML error page.
Common situations: Clients sending form-encoded or loosely quoted JSON with Content-Type: application/json; hand-crafted test tokens; upstream gateways returning an HTML 502 page where JSON was expected.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Expected a JSON object at the top level
- Cannot convert ${typeof value} to ${what}: expected bigint,
- Invalid hex UUID
- cannot serialize refs without a typespace
- cannot deserialize refs without a typespace
AI-assisted analysis of clockworklabs/SpacetimeDB@524b4487d9 (2026-08-16).
Data as JSON: /api/errors/b71ac94b3408d0ad.
Report an issue: GitHub.