thedotmack/claude-mem · critical · Error
plugin/hooks/codex-hooks.json contains unsupported Codex roo
Error message
plugin/hooks/codex-hooks.json contains unsupported Codex root key: ${rootKey} What it means
After loading plugin/hooks/codex-hooks.json, the build verifies every top-level key is in the allowed set {hooks}. Codex ignores unknown root keys (or errors opaquely), so any extraneous key would silently break the plugin under Codex. The guard names the offending key.
Source
Thrown at scripts/build-hooks.js:730
'plugin/scripts/bun-runner.js',
'plugin/sqlite/SessionStore.js',
'plugin/sqlite/observations/files.js',
'plugin/.claude-plugin/plugin.json',
'plugin/.codex-plugin/plugin.json',
'plugin/.mcp.json',
'.codex-plugin/plugin.json',
'.agents/plugins/marketplace.json',
];
for (const filePath of requiredDistributionFiles) {
if (!fs.existsSync(filePath)) {
throw new Error(`Missing required distribution file: ${filePath}`);
}
}
const codexHooks = JSON.parse(fs.readFileSync('plugin/hooks/codex-hooks.json', 'utf-8'));
const validCodexHookRootKeys = new Set(['hooks']);
for (const rootKey of Object.keys(codexHooks)) {
if (!validCodexHookRootKeys.has(rootKey)) {
throw new Error(`plugin/hooks/codex-hooks.json contains unsupported Codex root key: ${rootKey}`);
}
}
for (const eventName of Object.keys(codexHooks.hooks ?? {})) {
if (!validCodexHookEvents.has(eventName)) {
throw new Error(`plugin/hooks/codex-hooks.json contains unknown Codex hook event: ${eventName}`);
}
}
const codexMarketplace = JSON.parse(fs.readFileSync('.agents/plugins/marketplace.json', 'utf-8'));
const claudeMemMarketplaceEntry = (codexMarketplace.plugins ?? []).find((plugin) => plugin.name === 'claude-mem');
if (claudeMemMarketplaceEntry?.source?.path !== './plugin') {
throw new Error('.agents/plugins/marketplace.json must point claude-mem source.path at ./plugin so Codex loads the bundled plugin root');
}
const bundledMcp = JSON.parse(fs.readFileSync('plugin/.mcp.json', 'utf-8'));
const mcpSearchCommand = bundledMcp.mcpServers?.['mcp-search']?.args?.join(' ') ?? '';
if (!mcpSearchCommand.includes('.codex/plugins/cache/claude-mem-local/claude-mem')) {
throw new Error('plugin/.mcp.json mcp-search launcher must include Codex cache fallback for hosts that do not inject PLUGIN_ROOT');
}
if (!mcpSearchCommand.includes('plugins/cache/thedotmack/claude-mem')) {View on GitHub (pinned to d768ba3643)
Solutions
- Open plugin/hooks/codex-hooks.json and remove or nest the named root key — only 'hooks' may sit at the top level.
- If you need metadata, put it under a Codex-supported nested location or a separate sidecar file, not a new root key.
- Re-run the build; the check at scripts/build-hooks.js:728-731 must pass before downstream event validation.
Example fix
// before: plugin/hooks/codex-hooks.json
{
"hooks": { ... },
"events": { "SessionStart": [...] } // unsupported root key
}
// after — only 'hooks' at the root
{
"hooks": {
"SessionStart": [...] // moved under hooks
}
} Defensive patterns
Strategy: validation
Validate before calling
const codexHooks = JSON.parse(fs.readFileSync('plugin/hooks/codex-hooks.json','utf8'));
const badRoots = Object.keys(codexHooks).filter(k => k !== 'hooks');
if (badRoots.length) throw new Error('Unsupported codex root keys: ' + badRoots.join(',')); Type guard
function isCodexHooksRoot(obj): obj is { hooks: Record<string, unknown> } {
return obj && typeof obj === 'object' && 'hooks' in obj && Object.keys(obj).every(k => k === 'hooks');
} Try / catch
// Build-time only. Remove the unsupported root key (or nest it under 'hooks'). // Do not suppress — Codex would silently ignore the unknown key at runtime.
Prevention
- Treat plugin/hooks/codex-hooks.json as schema-locked: only a top-level 'hooks' object.
- Add a JSON-schema for codex-hooks.json and validate in CI.
- Don't copy fields from the Claude settings.json schema into the Codex file.
When it happens
Trigger: Editing plugin/hooks/codex-hooks.json and adding a top-level field (e.g. a copied 'events', 'config', or Claude-specific key) that Codex doesn't recognise. Cross-pasting from a Claude hooks JSON that has a different schema.
Common situations: Treating the Codex hooks file like the Claude settings.json schema. Adding metadata fields at the root. Merge that brings in a sibling key from another hook system.
Related errors
- plugin/hooks/codex-hooks.json contains unknown Codex hook ev
- Hand-edited shell string detected in ${filePath} (${dottedPa
- .agents/plugins/marketplace.json must point claude-mem sourc
- plugin/.mcp.json mcp-search launcher must include Codex cach
- Hand-edited shell string detected in ${filePath} (mcp-search
AI-assisted analysis of thedotmack/claude-mem@d768ba3643 (2026-08-12).
Data as JSON: /api/errors/0b6626bbeb974f06.
Report an issue: GitHub.