{"record":{"id":"49f15d0605156dd2","repo":"thedotmack/claude-mem","slug":"plugin-hooks-codex-hooks-json-contains-unknown-cod","errorCode":null,"errorMessage":"plugin/hooks/codex-hooks.json contains unknown Codex hook event: ${eventName}","messagePattern":"plugin/hooks/codex-hooks\\.json contains unknown Codex hook event: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"scripts/build-hooks.js","lineNumber":735,"sourceCode":"      'plugin/.mcp.json',\n      '.codex-plugin/plugin.json',\n      '.agents/plugins/marketplace.json',\n    ];\n    for (const filePath of requiredDistributionFiles) {\n      if (!fs.existsSync(filePath)) {\n        throw new Error(`Missing required distribution file: ${filePath}`);\n      }\n    }\n    const codexHooks = JSON.parse(fs.readFileSync('plugin/hooks/codex-hooks.json', 'utf-8'));\n    const validCodexHookRootKeys = new Set(['hooks']);\n    for (const rootKey of Object.keys(codexHooks)) {\n      if (!validCodexHookRootKeys.has(rootKey)) {\n        throw new Error(`plugin/hooks/codex-hooks.json contains unsupported Codex root key: ${rootKey}`);\n      }\n    }\n    for (const eventName of Object.keys(codexHooks.hooks ?? {})) {\n      if (!validCodexHookEvents.has(eventName)) {\n        throw new Error(`plugin/hooks/codex-hooks.json contains unknown Codex hook event: ${eventName}`);\n      }\n    }\n    const codexMarketplace = JSON.parse(fs.readFileSync('.agents/plugins/marketplace.json', 'utf-8'));\n    const claudeMemMarketplaceEntry = (codexMarketplace.plugins ?? []).find((plugin) => plugin.name === 'claude-mem');\n    if (claudeMemMarketplaceEntry?.source?.path !== './plugin') {\n      throw new Error('.agents/plugins/marketplace.json must point claude-mem source.path at ./plugin so Codex loads the bundled plugin root');\n    }\n    const bundledMcp = JSON.parse(fs.readFileSync('plugin/.mcp.json', 'utf-8'));\n    const mcpSearchCommand = bundledMcp.mcpServers?.['mcp-search']?.args?.join(' ') ?? '';\n    if (!mcpSearchCommand.includes('.codex/plugins/cache/claude-mem-local/claude-mem')) {\n      throw new Error('plugin/.mcp.json mcp-search launcher must include Codex cache fallback for hosts that do not inject PLUGIN_ROOT');\n    }\n    if (!mcpSearchCommand.includes('plugins/cache/thedotmack/claude-mem')) {\n      throw new Error('plugin/.mcp.json mcp-search launcher must include Claude cache fallback for hosts that do not inject PLUGIN_ROOT');\n    }\n    console.log('✓ All required distribution files present');\n\n    await verifyShellTemplateCanonical();","sourceCodeStart":717,"sourceCodeEnd":753,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/d768ba364302d12b76e69e4f021f0bb1d2d50ed6/scripts/build-hooks.js#L717-L753","documentation":"Iterates Object.keys(codexHooks.hooks) and asserts each event name is in validCodexHookEvents (SessionStart, UserPromptSubmit, PreToolUse, PermissionRequest, PostToolUse, etc., defined at scripts/build-hooks.js ~680). A typo'd or unsupported event would be silently ignored by Codex, so the build fails fast and names the bad event.","triggerScenarios":"Adding a hook under an event name Codex doesn't emit (e.g. 'ToolCall', 'OnIdle', or a misspelling like 'PostTooluser'). Copying a Claude-only event into the Codex hooks file.","commonSituations":"Typo in an event name. Assuming event parity between Claude and Codex hooks when they differ. Renaming an event upstream without updating this file.","solutions":["Open plugin/hooks/codex-hooks.json and rename the offending key to one of the supported events (see validCodexHookEvents in scripts/build-hooks.js).","If you intended a Claude-only behaviour, move that hook into the Claude settings file, not codex-hooks.json.","Re-run the build; the event-name check precedes the marketplace/MCP checks."],"exampleFix":"// before\n\"hooks\": { \"PostTooluser\": [ ... ] }   // typo\n\n// after\n\"hooks\": { \"PostToolUse\": [ ... ] }","handlingStrategy":"validation","validationCode":"const VALID = new Set(['SessionStart','UserPromptSubmit','PreToolUse','PermissionRequest','PostToolUse','Stop','Notification','SubagentStop']); // mirror the build's validCodexHookEvents\nconst hooks = JSON.parse(fs.readFileSync('plugin/hooks/codex-hooks.json','utf8')).hooks ?? {};\nconst bad = Object.keys(hooks).filter(k => !VALID.has(k));\nif (bad.length) throw new Error('Unknown codex hook events: ' + bad.join(','));","typeGuard":"function isKnownCodexEvent(name: string, valid: Set<string>): name is (typeof valid)[number] & string {\n  return valid.has(name);\n}","tryCatchPattern":"// Build-time only. Rename the event to one Codex emits, or move the hook into the\n// Claude settings file. Do not suppress.","preventionTips":["Keep the Codex hook event list in sync with Codex's emitted events; update validCodexHookEvents when Codex adds events.","Lint the codex-hooks.json keys against the allowed set in CI.","Don't assume Claude and Codex share the same event names."],"tags":["build","verification","codex","hooks","events"],"backgroundTag":null,"analyzedSha":"d768ba364302d12b76e69e4f021f0bb1d2d50ed6","analyzedAt":"2026-08-12T23:52:55.241Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}