{"record":{"id":"31d1e7ee64b1fb79","repo":"Yeachan-Heo/oh-my-codex","slug":"label-must-be-a-json-object","errorCode":null,"errorMessage":"${label} must be a JSON object.","messagePattern":"(.+?) must be a JSON object\\.","errorType":"validation","errorClass":"UltragoalError","httpStatus":null,"severity":"error","filePath":"src/cli/ultragoal.ts","lineNumber":235,"sourceCode":"const STEERING_KINDS = new Set<UltragoalSteeringMutationKind>(ULTRAGOAL_STEERING_MUTATION_KINDS);\nconst STEERING_SOURCES = new Set<UltragoalSteeringSource>(ULTRAGOAL_STEERING_SOURCES);\n\ntype CliSteerResult = Awaited<ReturnType<typeof steerUltragoal>>;\n\nfunction parseSteeringKind(raw: string | undefined): UltragoalSteeringMutationKind {\n  if (!raw) throw new UltragoalError('Missing --kind for structured ultragoal steer.');\n  if (!STEERING_KINDS.has(raw as UltragoalSteeringMutationKind)) throw new UltragoalError(`Invalid --kind: ${raw}. Expected one of ${Array.from(STEERING_KINDS).join(', ')}.`);\n  return raw as UltragoalSteeringMutationKind;\n}\n\nfunction parseSteeringSource(raw: string | undefined, fallback: UltragoalSteeringSource = 'cli'): UltragoalSteeringSource {\n  if (!raw) return fallback;\n  if (!STEERING_SOURCES.has(raw as UltragoalSteeringSource)) throw new UltragoalError(`Invalid --source: ${raw}. Expected one of ${Array.from(STEERING_SOURCES).join(', ')}.`);\n  return raw as UltragoalSteeringSource;\n}\n\nfunction assertPlainObject(value: unknown, label: string): Record<string, unknown> {\n  if (!value || typeof value !== 'object' || Array.isArray(value)) throw new UltragoalError(`${label} must be a JSON object.`);\n  return value as Record<string, unknown>;\n}\n\nfunction normalizeTargetGoalId(raw: Record<string, unknown>): string | undefined {\n  if (typeof raw.targetGoalId === 'string' && raw.targetGoalId.trim()) return raw.targetGoalId.trim();\n  if (Array.isArray(raw.targetGoalIds)) return raw.targetGoalIds.find((id): id is string => typeof id === 'string' && id.trim().length > 0)?.trim();\n  return undefined;\n}\n\nfunction normalizeSteeringProposal(raw: Record<string, unknown>, _fallbackDirectiveText?: string): UltragoalSteeringProposal {\n  const kind = parseSteeringKind(typeof raw.kind === 'string' ? raw.kind : undefined);\n  const source = parseSteeringSource(typeof raw.source === 'string' ? raw.source : undefined);\n  const after = raw.after && typeof raw.after === 'object' && !Array.isArray(raw.after)\n    ? raw.after as UltragoalSteeringAfterPayload\n    : undefined;\n  return {\n    kind,\n    source,","sourceCodeStart":217,"sourceCodeEnd":253,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/cli/ultragoal.ts#L217-L253","documentation":"assertPlainObject guards parsed JSON inputs (notably --directive-json) so the steering engine always receives a JSON object, never an array, scalar, or null. It is used with labels like '--directive-json' so the message says which input failed.","triggerScenarios":"Passing `--directive-json '[1,2]'`, `--directive-json '\"text\"'`, or `--directive-json 'null'` to `omx ultragoal steer`; a JSON file containing an array also triggers it.","commonSituations":"Reusing an exported array of proposals instead of a single object; tools emitting a top-level array; JSON files that wrap payloads in arrays by convention.","solutions":["Wrap the payload in an object: `{\"proposals\":[...]}` or send one proposal object with kind/evidence/rationale fields","Validate with `jq 'type'` — it must print \"object\" before passing the file"],"exampleFix":"# before\nomx ultragoal steer --directive-json '[{\"kind\":\"add_goal\"}]'\n# Error: --directive-json must be a JSON object.\n\n# after\nomx ultragoal steer --directive-json '{\"kind\":\"add_goal\",\"evidence\":\"e\",\"rationale\":\"r\"}'","handlingStrategy":"type-guard","validationCode":"const parsed = JSON.parse(directiveJson);\nif (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n  console.error('--directive-json must be an object, not an array/scalar'); process.exit(2);\n}","typeGuard":"function isPlainObject(v: unknown): v is Record<string, unknown> {\n  return !!v && typeof v === 'object' && !Array.isArray(v);\n}","tryCatchPattern":null,"preventionTips":["Check `jq 'type' == \"object\"` before passing files","Build directives with JSON.stringify of an object literal, never an array"],"tags":["cli","json","type-validation","ultragoal"],"backgroundTag":"json-shape-validation-failed","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}