coleam00/Archon · warning
${label} must be a JSON-encoded object
Error message
${label} must be a JSON-encoded object What it means
The run-workflow route decodes multipart 'tiers' and 'aliases' fields via decodeObjectField; each must be absent or a JSON-encoded object string. If the field is present but is not a string (e.g. an uploaded File or an array of parts from { all: true }), the route returns 400 with '<label> must be a JSON-encoded object' before even attempting JSON.parse.
Source
Thrown at packages/server/src/routes/api.ts:3440
}
let decoded: unknown;
try {
decoded = JSON.parse(rawInputs);
} catch (parseErr: unknown) {
getLog().warn({ err: parseErr, workflowName }, 'run_workflow.inputs_parse_failed');
return apiError(c, 400, 'inputs must be a JSON-encoded object of string values');
}
const parsed = parseRunInputsField(decoded);
if (!parsed.ok) return apiError(c, 400, parsed.error);
workflowInputs = parsed.inputs;
}
const decodeObjectField = (
raw: string | File | (string | File)[] | undefined,
label: 'tiers' | 'aliases'
): { ok: true; value?: unknown } | { ok: false; error: string } => {
if (raw === undefined) return { ok: true };
if (typeof raw !== 'string') {
return { ok: false, error: `${label} must be a JSON-encoded object` };
}
try {
return { ok: true, value: JSON.parse(raw) as unknown };
} catch (parseErr: unknown) {
getLog().warn(
{ err: parseErr, workflowName, field: label },
'run_workflow.model_overrides_parse_failed'
);
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,View on GitHub (pinned to 0773b97458)
Solutions
- Send tiers/aliases as plain text fields containing JSON: -F 'tiers={"default":2}'.
- Send each field exactly once — no duplicate parts.
- Do not attach these fields as files; the route accepts strings only.
- If you have them in files, read and inline the contents as the field value.
Example fix
// before -F 'tiers=@tiers.json' // arrives as a File part // after -F "tiers=$(cat tiers.json)" // arrives as a string part
Defensive patterns
Strategy: validation
Validate before calling
function appendJsonField(form: FormData, label: 'tiers' | 'aliases', value: Record<string, unknown>): void {
form.append(label, JSON.stringify(value)); // plain string part, not a file, appended once
}
// pre-send check:
const raw = form.get('tiers');
if (raw !== null && (typeof raw !== 'string' || !isValidJsonObject(raw))) throw new Error('tiers must be a JSON-encoded object string'); Type guard
function isJsonEncodedObject(s: unknown): s is string {
if (typeof s !== 'string') 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.includes('must be a JSON-encoded object')) {
throw new Error('tiers/aliases must be plain text fields containing a JSON object — not file uploads, not duplicates');
}
} catch (err) { /* rebuild form fields as strings */ } Prevention
- Append tiers/aliases as text fields (form.append(name, jsonString)), never with a filename/file blob.
- Append each field exactly once; duplicate parts parse into arrays and fail the string check.
- Lint the JSON with jq before sending.
- Inline file contents into the field value when the data lives in a file.
When it happens
Trigger: Sending tiers or aliases as a file part (name="tiers"; filename=...) instead of a plain text form field, or duplicating the field so parseBody({ all: true }) yields an array, or sending a non-string part type.
Common situations: Uploading a tiers.json file via -F 'tiers=@tiers.json' instead of -F 'tiers=<json>'; accidentally appending the same field twice; generic form builders that attach files for object payloads.
Related errors
- inputs must be a JSON-encoded object of string values
- config 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/cbb8eafa1d91d3dc.
Report an issue: GitHub.