{"record":{"id":"0ca33ab4a4f8d47a","repo":"Yeachan-Heo/oh-my-codex","slug":"handoff-json-must-resolve-to-a-json-object","errorCode":null,"errorMessage":"--handoff-json must resolve to a JSON object.","messagePattern":"--handoff-json must resolve to a JSON object\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/cli/autopilot.ts","lineNumber":51,"sourceCode":"  const result = args[index + 1];\n  if (!result || result.startsWith('--')) throw new Error(`Missing value for ${flag}.`);\n  return result;\n}\n\nfunction positionalTask(args: readonly string[]): string {\n  const valueFlags = new Set(['--task', '--session', '--to', '--handoff-json']);\n  const words: string[] = [];\n  for (let i = 0; i < args.length; i += 1) {\n    if (valueFlags.has(args[i])) { i += 1; continue; }\n    if (!args[i].startsWith('--')) words.push(args[i]);\n  }\n  return words.join(' ').trim();\n}\n\nasync function jsonInput(raw: string): Promise<Record<string, unknown>> {\n  const text = raw.trim().startsWith('{') ? raw : await readFile(raw, 'utf-8');\n  const parsed = JSON.parse(text) as unknown;\n  if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) throw new Error('--handoff-json must resolve to a JSON object.');\n  return parsed as Record<string, unknown>;\n}\n\nfunction assertBoundHandoffIdentity(handoff: Record<string, unknown>, cwd: string, sessionId?: string): void {\n  if (typeof handoff.session_id === 'string' && handoff.session_id !== sessionId) {\n    throw new Error('Autopilot handoff session_id does not match the selected session.');\n  }\n  if (typeof handoff.workingDirectory === 'string' && handoff.workingDirectory !== cwd) {\n    throw new Error('Autopilot handoff workingDirectory does not match the selected workspace.');\n  }\n  handoff.session_id = sessionId;\n  handoff.workingDirectory = cwd;\n}\n\nasync function readAutopilot(cwd: string, sessionId?: string) {\n  return sessionId\n    ? readModeStateForExplicitSession('autopilot', sessionId, cwd)\n    : readModeState('autopilot', cwd);","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/cli/autopilot.ts#L33-L69","documentation":"Thrown by jsonInput() when the --handoff-json argument (inline string or file path) parses to something other than a plain JSON object. The autopilot advance command requires a handoff payload that is a JSON object; arrays, strings, numbers, null, or empty input all fail this check.","triggerScenarios":"Calling `omx autopilot advance --handoff-json <x>` where x is either an inline string not starting with '{' whose file content parses to a non-object (e.g. a JSON array or string), or a path to a file containing `[...]`, `\"text\"`, `123`, or invalid JSON (JSON.parse would throw first for invalid JSON; this error fires for valid-but-non-object JSON).","commonSituations":"Agent pipelines writing a JSON array of steps as the handoff, saving a handoff exported from another tool that wraps it in quotes, or passing an empty file. Also hitting this when JSON.parse succeeds on a top-level scalar.","solutions":["Check the handoff payload with `cat <file>` or paste it into a JSON validator and confirm the top-level value is an object `{...}`, not an array or string","If the payload is an array, wrap it: {\"steps\": [...]}, or restructure to the expected handoff schema (task, session_id, workingDirectory keys)","If passing inline, ensure the argument starts with `{` — otherwise it is treated as a file path and readFile fails or reads wrong content","Confirm the file path exists and is readable so you are validating the intended payload"],"exampleFix":"// before\nomx autopilot advance --to ralplan --handoff-json '[{\"step\":1}]'\n// after\nomx autopilot advance --to ralplan --handoff-json '{\"steps\":[{\"step\":1}]}'}","handlingStrategy":"validation","validationCode":"function isJsonObjectFile(s: string): boolean {\n  const text = s.trim().startsWith('{') ? s : require('fs').readFileSync(s, 'utf-8');\n  const p = JSON.parse(text);\n  return !!p && typeof p === 'object' && !Array.isArray(p);\n}\nif (!isJsonObjectFile(args.handoffJson)) failFast('handoff must be a JSON object');","typeGuard":"function isJsonObject(v: unknown): v is Record<string, unknown> {\n  return !!v && typeof v === 'object' && !Array.isArray(v);\n}","tryCatchPattern":"try { await autopilotCommand(['advance','--to','ralplan','--handoff-json',p]); } catch (e) { if (e.message.includes('must resolve to a JSON object')) printPayloadError(p); else throw e; }","preventionTips":["Validate handoff files with a JSON schema before invoking the CLI","Keep handoff writers typed as Record<string, unknown> so arrays can't be emitted"],"tags":["cli","json-validation","handoff","autopilot"],"backgroundTag":"json-object-validation","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}