{"record":{"id":"5db0ffd1ddc84755","repo":"Yeachan-Heo/oh-my-codex","slug":"enabled-must-be-boolean","errorCode":null,"errorMessage":"`enabled` must be boolean","messagePattern":"`enabled` must be boolean","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/cli/tmux-hook.ts","lineNumber":115,"sourceCode":"function 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;\n  if (!Array.isArray(allowedModes) || allowedModes.length === 0 || allowedModes.some(v => typeof v !== 'string')) {\n    throw new Error('`allowed_modes` must be a non-empty string array');\n  }\n","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/cli/tmux-hook.ts#L97-L133","documentation":"The tmux-hook config's top-level `enabled` field is missing or not strictly boolean (true/false). The validator rejects truthy values like 1, \"true\", or \"yes\" because only strict booleans are accepted.","triggerScenarios":"Config contains \"enabled\": 1, \"enabled\": \"true\", or omits the key entirely (undefined fails the check).","commonSituations":"YAML-style habits (yes/no) carried into JSON; env-templated configs substituting strings; omitting the key assuming it defaults to true.","solutions":["Set \"enabled\": true or false exactly (unquoted, no 1/0)","Run omx tmux-hook init to regenerate a valid config","If templating, cast to boolean before serializing"],"exampleFix":"// before\n{\"enabled\": \"true\", ...}\n// after\n{\"enabled\": true, ...}","handlingStrategy":"type-guard","validationCode":"const cfg = JSON.parse(text);\nif (typeof cfg.enabled !== 'boolean') throw new Error('fix enabled before running omx');","typeGuard":"const isBool = (v: unknown): v is boolean => v === true || v === false;","tryCatchPattern":"try { await loadConfig(); } catch (e) { if (e instanceof Error && e.message.includes('`enabled`')) { repairConfig(); } }","preventionTips":["Never quote booleans in JSON config","Use omx tmux-hook init output as the canonical template"],"tags":["config","validation","boolean","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"}