coleam00/Archon · error
Request body is not valid JSON — send {"comment": "..."} or
Error message
Request body is not valid JSON — send {"comment": "..."} or no body What it means
The server's POST approve endpoint (packages/server/src/routes/api.ts:3921) accepts an optional JSON body of shape {comment: string}. When a request sends a non-empty body that JSON.parse cannot parse, it rejects with 400 instead of guessing at malformed input, keeping the approval-gate API strict and auditable.
Source
Thrown at packages/server/src/routes/api.ts:3921
'Approve or reject the child run instead.',
true
);
if (approveBlocker) {
return apiError(c, 400, approveBlocker);
}
// Distinguish "no body sent" (legitimate bare approve) from "body sent but
// unparseable" (client bug). Since #2074 a bare approve FINALIZES a
// signal-bearing loop gate, so silently coercing a malformed body to {}
// would discard intended feedback and finalize undiagnosed — reject it.
const rawBody = await c.req.text();
let body: { comment?: string } = {};
if (rawBody.trim().length > 0) {
try {
body = JSON.parse(rawBody) as { comment?: string };
} catch (parseError) {
getLog().warn({ err: parseError, runId }, 'api.approve_body_parse_failed');
return apiError(
c,
400,
'Request body is not valid JSON — send {"comment": "..."} or no body'
);
}
}
// Shared gate logic (events, telemetry, metadata staging) — the run stays
// 'paused' with metadata.approval.resolved = 'approved' (#2075). The
// pre-checks above map the common error cases to 400s; approveWorkflow
// re-validates and anything it throws past them is a 500.
// The raw (possibly undefined) comment is passed through — approveWorkflow
// defaults the recorded comment internally, but "no feedback" must survive
// so a signal-bearing interactive-loop gate finalizes instead of re-running
// (#2074, loop_feedback_given).
await approveWorkflow(runId, body.comment);
// Auto-resume: dispatch to the orchestrator so the workflow continues
// without requiring the user to re-run the workflow command. Mirrors
// what `workflowApproveCommand` does in the CLI. RequiresView on GitHub (pinned to 0773b97458)
Solutions
- Fix the client to send valid JSON: {"comment": "..."} with Content-Type: application/json
- Omit the request body entirely — the endpoint allows no body
- Inspect the server log keyed 'api.approve_body_parse_failed' for the exact parse error
- Use a JSON-aware client flag, e.g. curl -H 'Content-Type: application/json' -d '{"comment":"ok"}'
Example fix
// before
curl -X POST $URL/runs/$RUN/approve -d 'ship it'
// after
curl -X POST $URL/runs/$RUN/approve -H 'Content-Type: application/json' -d '{"comment":"ship it"}' Defensive patterns
Strategy: validation
Validate before calling
function buildApproveBody(comment) {
if (comment === undefined) return null; // omit body entirely
const json = JSON.stringify({ comment });
JSON.parse(json); // prove serializable before sending
return json;
} Type guard
function isApproveBody(b: unknown): b is { comment?: string } {
return typeof b === 'object' && b !== null &&
!('comment' in b) || typeof (b as any).comment === 'string';
} Try / catch
try {
const res = await fetch(url, { method:'POST', headers:{'Content-Type':'application/json'}, body: bodyJson ?? undefined });
if (res.status === 400) throw new Error('approve body rejected as invalid JSON');
} catch (e) { /* log and fall back to no-body approve */ } Prevention
- Always send bodies with JSON.stringify, never string concatenation
- Set Content-Type: application/json on POSTs
- Omit the body when there is no comment
- Validate payloads with JSON.parse locally before sending
When it happens
Trigger: POSTing to the run-approve route with a non-empty raw body that is not valid JSON (e.g. a bare comment string like "looks good", trailing commas, single quotes, or an HTML/plain-text body from curl without a Content-Type set). An empty body is allowed.
Common situations: curl -d 'approved' without quoting JSON; clients setting text/plain or form bodies; templated shell commands where the comment contains unescaped quotes that break JSON; automation scripts that pass a raw message as the body.
Related errors
- Request body is not valid JSON — send {"reason": "..."} or n
- Request body is not valid JSON — send {"decision": "...", "t
- Invalid JSON in request body
- Expected directory listing from ${url}, got a single file
- owner returned an invalid response
AI-assisted analysis of coleam00/Archon@0773b97458 (2026-09-01).
Data as JSON: /api/errors/190911ba375e9342.
Report an issue: GitHub.