coleam00/Archon · warning
config must be a JSON-encoded object
Error message
config must be a JSON-encoded object
What it means
On the run-workflow route, the 'config' field must be a JSON-encoded object string. The route rejects it with 400 and this exact message in two cases: the body value is not a string (non-string part type), or JSON.parse of the string throws; the parse failure is logged as run_workflow.config_parse_failed.
Source
Thrown at packages/server/src/routes/api.ts:3468
return { ok: false, error: `${label} must be a JSON-encoded object` };
}
};
const decodedTiers = decodeObjectField(body.tiers, 'tiers');
if (!decodedTiers.ok) return apiError(c, 400, decodedTiers.error);
const decodedAliases = decodeObjectField(body.aliases, 'aliases');
if (!decodedAliases.ok) return apiError(c, 400, decodedAliases.error);
const parsedOverrides = parseRunModelOverridesFields(
decodedTiers.value,
decodedAliases.value
);
if (!parsedOverrides.ok) return apiError(c, 400, parsedOverrides.error);
workflowModelOverrides = parsedOverrides.overrides;
if (body.config !== undefined) {
if (typeof body.config !== 'string') {
return apiError(c, 400, 'config must be a JSON-encoded object');
}
let decodedConfig: unknown;
try {
decodedConfig = JSON.parse(body.config) as unknown;
} catch (parseErr: unknown) {
getLog().warn({ err: parseErr, workflowName }, 'run_workflow.config_parse_failed');
return apiError(c, 400, 'config must be a JSON-encoded object');
}
try {
workflowRunConfig = parseWorkflowRunConfig(decodedConfig, {
kind: 'http',
label: 'inline',
});
} catch (error) {
return apiError(c, 400, (error as Error).message);
}
}
const rawFiles = body.files;
const fileList: (string | File)[] = Array.isArray(rawFiles)View on GitHub (pinned to 0773b97458)
Solutions
- Send config as one plain text field with strict JSON: -F 'config={"timeout":300}'.
- Validate with JSON.parse locally and lint the JSON (e.g. jq .) before sending.
- Check shell quoting — wrap the JSON in single quotes in bash.
- Send each config field only once as a string part, never as a file.
Example fix
// before
-F 'config={timeout: 300}' // invalid JSON
// after
-F 'config={"timeout":300}' // valid JSON object Defensive patterns
Strategy: validation
Validate before calling
function encodeConfig(config: Record<string, unknown>): string {
const json = JSON.stringify(config);
JSON.parse(json); // round-trip guarantee
return json;
}
form.append('config', encodeConfig({ timeout: 300 })); Type guard
function isJsonEncodedConfig(s: unknown): s is string {
if (typeof s !== 'string' || !s) return false;
try {
const v: unknown = JSON.parse(s);
return typeof v === 'object' && v !== null && !Array.isArray(v);
} catch { return false; }
} Try / catch
try {
const res = await runWorkflow(form);
if (res.status === 400 && (await res.json()).error === 'config must be a JSON-encoded object') {
throw new Error('config must be one plain string field holding strict JSON');
}
} catch (err) { /* re-encode config with JSON.stringify and retry */ } Prevention
- Always produce config with JSON.stringify; no comments, single quotes, or trailing commas.
- Append config once as a text field, not as a file part.
- Validate with jq or JSON.parse before sending.
- Beware shell quoting: single-quote the JSON string in bash commands.
When it happens
Trigger: Sending config as a non-string part (file/array), or as invalid JSON text (trailing comma, single quotes, YAML), when starting a workflow run via multipart.
Common situations: Shell quoting stripping double quotes from config JSON; pasting config with comments or trailing commas; template engines rendering the field empty or with newlines that break the encoder; sending config as a file upload.
Related errors
- inputs must be a JSON-encoded object of string values
- ${label} must be a JSON-encoded object
- Invalid JSON in request body
- ❌ **Composed fan-out failed** (node `${node.id}`): ${current
- Cannot execute run '${detachedPreCreatedRun.id}': it belongs
AI-assisted analysis of coleam00/Archon@0773b97458 (2026-09-01).
Data as JSON: /api/errors/f92e503bd496a18e.
Report an issue: GitHub.