{"record":{"id":"2139a1b37a2cc650","repo":"Yeachan-Heo/oh-my-codex","slug":"tmux-hook-config-must-be-a-json-object","errorCode":null,"errorMessage":"tmux-hook config must be a JSON object","messagePattern":"tmux-hook config must be a JSON object","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/cli/tmux-hook.ts","lineNumber":111,"sourceCode":"function omxDir(cwd = process.cwd()): string {\n  return omxRoot(cwd);\n}\n\nfunction tmuxHookConfigPath(cwd = process.cwd()): string {\n  return join(omxDir(cwd), 'tmux-hook.json');\n}\n\nfunction tmuxHookStatePath(cwd = process.cwd()): string {\n  return join(omxDir(cwd), 'state', 'tmux-hook-state.json');\n}\n\nfunction tmuxHookLogPath(cwd = process.cwd()): string {\n  return join(omxDir(cwd), 'logs', `tmux-hook-${new Date().toISOString().split('T')[0]}.jsonl`);\n}\n\nfunction parseConfig(raw: unknown): TmuxHookConfig {\n  if (!raw || typeof raw !== 'object') {\n    throw new Error('tmux-hook config must be a JSON object');\n  }\n  const parsed = raw as Record<string, unknown>;\n  if (parsed.enabled !== true && parsed.enabled !== false) {\n    throw new Error('`enabled` must be boolean');\n  }\n  const target = parsed.target;\n  if (!target || typeof target !== 'object') {\n    throw new Error('`target` is required');\n  }\n  const targetObj = target as Record<string, unknown>;\n  if (targetObj.type !== 'session' && targetObj.type !== 'pane') {\n    throw new Error('`target.type` must be \"session\" or \"pane\"');\n  }\n  if (typeof targetObj.value !== 'string' || targetObj.value.trim() === '') {\n    throw new Error('`target.value` must be a non-empty string');\n  }\n\n  const allowedModes = parsed.allowed_modes;","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/cli/tmux-hook.ts#L93-L129","documentation":"The tmux-hook config file was parsed as JSON but the result is not an object (null, array, number, string, or boolean). parseConfig requires a top-level JSON object before field-by-field validation proceeds.","triggerScenarios":"A config file containing e.g. `[]`, `null`, `\"text\"`, or `123` at `.omx/tmux-hook.json` (or wherever tmuxHookConfigPath points); also a JSON.parse result that yields a non-object.","commonSituations":"Hand-editing the config and replacing the object with a bare value; truncated or template-generated config files.","solutions":["Restore the config to a JSON object starting with { }","Regenerate with omx tmux-hook init","Validate the file with jq type check before running"],"exampleFix":"// before\n// .omx/tmux-hook.json\nnull\n// after\n{\"enabled\":true,\"target\":{\"type\":\"session\",\"value\":\"main\"},\"allowed_modes\":[\"vi\"]}","handlingStrategy":"type-guard","validationCode":"const raw = JSON.parse(await readFile(cfg, 'utf-8'));","typeGuard":"function isTmuxHookConfigLike(v: unknown): v is Record<string, unknown> {\n  return typeof v === 'object' && v !== null && !Array.isArray(v);\n}","tryCatchPattern":"try { await runTmuxHook(); } catch (e) { if (e instanceof Error && e.message.includes('must be a JSON object')) fixAndRegenerateConfig(); else throw e; }","preventionTips":["Validate with jq before running: jq type .omx/tmux-hook.json should print object","Regenerate config with omx tmux-hook init after manual edits"],"tags":["config","json","validation","tmux-hook"],"backgroundTag":"config-schema-validation-failed","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}