{"record":{"id":"f92e503bd496a18e","repo":"coleam00/Archon","slug":"config-must-be-a-json-encoded-object","errorCode":null,"errorMessage":"config must be a JSON-encoded object","messagePattern":"config must be a JSON-encoded object","errorType":"http","errorClass":null,"httpStatus":400,"severity":"warning","filePath":"packages/server/src/routes/api.ts","lineNumber":3468,"sourceCode":"          return { ok: false, error: `${label} must be a JSON-encoded object` };\n        }\n      };\n      const decodedTiers = decodeObjectField(body.tiers, 'tiers');\n      if (!decodedTiers.ok) return apiError(c, 400, decodedTiers.error);\n      const decodedAliases = decodeObjectField(body.aliases, 'aliases');\n      if (!decodedAliases.ok) return apiError(c, 400, decodedAliases.error);\n      const parsedOverrides = parseRunModelOverridesFields(\n        decodedTiers.value,\n        decodedAliases.value\n      );\n      if (!parsedOverrides.ok) return apiError(c, 400, parsedOverrides.error);\n      workflowModelOverrides = parsedOverrides.overrides;\n\n      if (body.config !== undefined) {\n        if (typeof body.config !== 'string') {\n          return apiError(c, 400, 'config must be a JSON-encoded object');\n        }\n        let decodedConfig: unknown;\n        try {\n          decodedConfig = JSON.parse(body.config) as unknown;\n        } catch (parseErr: unknown) {\n          getLog().warn({ err: parseErr, workflowName }, 'run_workflow.config_parse_failed');\n          return apiError(c, 400, 'config must be a JSON-encoded object');\n        }\n        try {\n          workflowRunConfig = parseWorkflowRunConfig(decodedConfig, {\n            kind: 'http',\n            label: 'inline',\n          });\n        } catch (error) {\n          return apiError(c, 400, (error as Error).message);\n        }\n      }\n\n      const rawFiles = body.files;\n      const fileList: (string | File)[] = Array.isArray(rawFiles)","sourceCodeStart":3450,"sourceCodeEnd":3486,"githubUrl":"https://github.com/coleam00/Archon/blob/0773b9745896ef0612e709c80845a0f7db315b19/packages/server/src/routes/api.ts#L3450-L3486","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\n-F 'config={timeout: 300}'          // invalid JSON\n// after\n-F 'config={\"timeout\":300}'         // valid JSON object","handlingStrategy":"validation","validationCode":"function encodeConfig(config: Record<string, unknown>): string {\n  const json = JSON.stringify(config);\n  JSON.parse(json); // round-trip guarantee\n  return json;\n}\nform.append('config', encodeConfig({ timeout: 300 }));","typeGuard":"function isJsonEncodedConfig(s: unknown): s is string {\n  if (typeof s !== 'string' || !s) return false;\n  try {\n    const v: unknown = JSON.parse(s);\n    return typeof v === 'object' && v !== null && !Array.isArray(v);\n  } catch { return false; }\n}","tryCatchPattern":"try {\n  const res = await runWorkflow(form);\n  if (res.status === 400 && (await res.json()).error === 'config must be a JSON-encoded object') {\n    throw new Error('config must be one plain string field holding strict JSON');\n  }\n} catch (err) { /* re-encode config with JSON.stringify and retry */ }","preventionTips":["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."],"tags":["http-400","json","workflow","input-validation"],"backgroundTag":"invalid-json-field","analyzedSha":"0773b9745896ef0612e709c80845a0f7db315b19","analyzedAt":"2026-09-01T02:28:07.064Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}