{"record":{"id":"c1c6c542dc7a69a2","repo":"Yeachan-Heo/oh-my-codex","slug":"target-type-must-be-session-or-pane","errorCode":null,"errorMessage":"`target.type` must be \"session\" or \"pane\"","messagePattern":"`target\\.type` must be \"session\" or \"pane\"","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/cli/tmux-hook.ts","lineNumber":123,"sourceCode":"function 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\n  const cooldown = parsed.cooldown_ms;\n  const maxInjections = parsed.max_injections_per_session;\n  if (typeof cooldown !== 'number' || cooldown < 0 || !Number.isFinite(cooldown)) {\n    throw new Error('`cooldown_ms` must be a non-negative number');\n  }\n  if (typeof maxInjections !== 'number' || maxInjections < 1 || !Number.isFinite(maxInjections)) {\n    throw new Error('`max_injections_per_session` must be >= 1');\n  }","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/cli/tmux-hook.ts#L105-L141","documentation":"The config's target.type field must be exactly the string \"session\" or \"pane\"; anything else (including missing/undefined, capitalized forms, or plurals) is rejected.","triggerScenarios":"\"type\": \"window\", \"type\": \"Session\", or omitting type so it is undefined.","commonSituations":"Users guessing the tmux terminology (tmux has windows/sessions/panes and only two are supported); case or spelling mistakes.","solutions":["Use exactly \"session\" or \"pane\" (lowercase)","Pick pane if you want injection into a specific pane id like %3","Re-read the supported target types via omx tmux-hook help"],"exampleFix":"// before\n{\"target\": {\"type\": \"window\", \"value\": \"0\"}}\n// after\n{\"target\": {\"type\": \"session\", \"value\": \"omx\"}}","handlingStrategy":"validation","validationCode":"const t = cfg.target?.type;\nif (t !== 'session' && t !== 'pane') throw new Error('bad target.type');","typeGuard":"const isTargetType = (v: unknown): v is 'session' | 'pane' => v === 'session' || v === 'pane';","tryCatchPattern":"try { await loadConfig(); } catch (e) { if (e instanceof Error && e.message.includes('target.type')) fixType(); }","preventionTips":["Only session and pane are supported; tmux windows are not","Use exact lowercase strings"],"tags":["config","validation","enum","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"}