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. Requires

View on GitHub (pinned to 0773b97458)

Solutions

  1. Fix the client to send valid JSON: {"comment": "..."} with Content-Type: application/json
  2. Omit the request body entirely — the endpoint allows no body
  3. Inspect the server log keyed 'api.approve_body_parse_failed' for the exact parse error
  4. 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

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


AI-assisted analysis of coleam00/Archon@0773b97458 (2026-09-01). Data as JSON: /api/errors/190911ba375e9342. Report an issue: GitHub.