coleam00/Archon · warning
Invalid JSON in request body
Error message
Invalid JSON in request body
What it means
The conversation-message route expected a JSON body ({ message: string }) but c.req.json() threw because the body is not valid JSON. The server logs message.json_parse_failed with the parse error and conversationId, then returns 400 with this message, indicating a malformed client request rather than a server fault.
Source
Thrown at packages/server/src/routes/api.ts:2928
}
const fileEntries = fileList.filter((e): e is File => e instanceof File);
if (fileEntries.length > 0) {
const result = await persistUploadedFiles(conversationId, fileEntries);
if (!result.ok) {
return c.json({ error: result.error }, result.status);
}
savedFiles = result.savedFiles;
uploadDir = result.uploadDir;
getLog().info({ conversationId, fileCount: savedFiles.length }, 'message.files_uploaded');
}
} else {
let body: { message?: unknown };
try {
body = await c.req.json();
} catch (parseErr: unknown) {
getLog().warn({ err: parseErr, conversationId }, 'message.json_parse_failed');
return c.json({ error: 'Invalid JSON in request body' }, 400);
}
if (typeof body.message !== 'string' || !body.message) {
return c.json({ error: 'message must be a non-empty string' }, 400);
}
message = body.message;
}
// Look up conversation for message persistence
let conv: Awaited<ReturnType<typeof conversationDb.findConversationByPlatformId>> = null;
try {
conv = await conversationDb.findConversationByPlatformId(conversationId);
} catch (e: unknown) {
getLog().error({ err: e, conversationId }, 'conversation_lookup_failed');
}
// Persist user message and pass DB ID to adapter for assistant message persistence
if (conv) {View on GitHub (pinned to 0773b97458)
Solutions
- JSON.stringify the payload object and send it as the body with Content-Type: application/json.
- Validate the body parses locally (JSON.parse in a test) before sending.
- Check for body truncation through proxies/timeouts on large messages.
- If the payload includes files, switch to the multipart variant of the route instead of embedding binary data in JSON.
Example fix
// before
await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: `{ message: "${msg}" }` });
// after
await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: msg }) }); Defensive patterns
Strategy: validation
Validate before calling
function buildMessageBody(message: string): string {
if (typeof message !== 'string' || !message) throw new Error('message must be a non-empty string');
return JSON.stringify({ message }); // validate locally what the route expects
}
const body = buildMessageBody(msg); // JSON.parse(body) round-trips safely Type guard
function isValidMessageBody(raw: string): boolean {
try {
const parsed: unknown = JSON.parse(raw);
return typeof (parsed as { message?: unknown })?.message === 'string' && (parsed as { message: string }).message.length > 0;
} catch { return false; }
} Try / catch
try {
const res = await sendMessage(body);
if (res.status === 400) {
const { error } = await res.json();
if (error === 'Invalid JSON in request body') throw new Error('Client sent non-JSON body; check JSON.stringify/content-type');
}
} catch (err) { /* surface client-side serialization bug */ } Prevention
- Always JSON.stringify request objects; never interpolate strings into JSON templates.
- Send Content-Type: application/json together with a stringified body.
- Validate the payload with JSON.parse in tests before shipping the client call.
- Use the multipart route for file payloads instead of embedding binary in JSON.
When it happens
Trigger: POSTing to the conversation message endpoint with Content-Type application/json but a body that is empty, truncated, or invalid JSON (unquoted keys, trailing commas, plain text, binary data).
Common situations: Client sending form-encoded or raw-text body while claiming JSON content type; a proxy/CDN truncating the body; string-template code building JSON without escaping; forgetting JSON.stringify on the payload.
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
- Invalid multipart form data
- inputs must be a JSON-encoded object of string values
- ${label} must be a JSON-encoded object
- config must be a JSON-encoded object
- Request body is not valid JSON — send {"comment": "..."} or
AI-assisted analysis of coleam00/Archon@0773b97458 (2026-09-01).
Data as JSON: /api/errors/434554c877397da6.
Report an issue: GitHub.