{"record":{"id":"271b6fb227ea54cd","repo":"Yeachan-Heo/oh-my-codex","slug":"hooks-validation-failed-failed-plugin-failed","errorCode":null,"errorMessage":"hooks validation failed (${failed} plugin${failed === 1 ? '' : 's'})","messagePattern":"hooks validation failed \\((.+?) plugin(.+?)\\)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/cli/hooks.ts","lineNumber":143,"sourceCode":"  const plugins = await discoverHookPlugins(cwd);\n  if (plugins.length === 0) {\n    console.log('No plugins found. Run: omx hooks init');\n    return;\n  }\n\n  let failed = 0;\n  for (const plugin of plugins) {\n    const result = await validateHookPluginExport(plugin.filePath);\n    if (result.valid) {\n      console.log(`✓ ${plugin.fileName}`);\n    } else {\n      failed += 1;\n      console.log(`✗ ${plugin.fileName}: ${result.reason || 'invalid export'}`);\n    }\n  }\n\n  if (failed > 0) {\n    throw new Error(`hooks validation failed (${failed} plugin${failed === 1 ? '' : 's'})`);\n  }\n}\n\nfunction normalizeDispatchResult(result: unknown): {\n  enabled: boolean;\n  reason: string;\n  results: Record<string, unknown>[];\n} {\n  if (!result || typeof result !== 'object') {\n    return { enabled: false, reason: 'invalid_result', results: [] };\n  }\n\n  const obj = result as Record<string, unknown>;\n  const results = Array.isArray(obj.results)\n    ? obj.results.filter((item): item is Record<string, unknown> => !!item && typeof item === 'object')\n    : [];\n\n  return {","sourceCodeStart":125,"sourceCodeEnd":161,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/cli/hooks.ts#L125-L161","documentation":"During `hooks validate`, one or more hook plugin files failed validation (invalid export or a reported reason per file). The command prints each failing plugin with its reason, then throws this aggregate error indicating how many failed.","triggerScenarios":"Running `omx hooks validate` when at least one plugin file in the hooks directory exports something invalid (wrong shape, missing default export, or throwing during load), so `failed > 0`.","commonSituations":"Editing a hook plugin and breaking its export shape; adding a third-party plugin with an incompatible API; Node version changes breaking a plugin at load time.","solutions":["Read the per-plugin `✗ <file>: <reason>` lines printed before the error to identify failures","Fix each plugin's export to match the expected hook plugin shape (see a known-good plugin for reference)","Remove or quarantine broken plugins and re-run `omx hooks validate`","Check the plugin's runtime errors (e.g. syntax errors, missing deps) if reason is 'invalid export'"],"exampleFix":"// before (broken plugin)\nexport const config = { enabled: true };\n\n// after\nexport default {\n  enabled: true,\n  dispatch(result) { return result; },\n};","handlingStrategy":"validation","validationCode":"import { validateHooks } from './src/cli/hooks';\n// Dry-run style: wrap validate and inspect per-plugin reasons before it throws\nconst results = listPluginFiles().map(f => ({ f, ok: checkPluginExports(f) }));\nconst failing = results.filter(r => !r.ok);\nif (failing.length) console.error('Failing plugins:', failing.map(r => r.f));","typeGuard":"function isValidHookPlugin(mod: unknown): mod is { default: { enabled: boolean; dispatch: (r: unknown) => unknown } } {\n  const m = mod as { default?: unknown };\n  const d = m?.default;\n  return !!d && typeof (d as { enabled?: unknown }).enabled === 'boolean'\n    && typeof (d as { dispatch?: unknown }).dispatch === 'function';\n}","tryCatchPattern":"try {\n  await validateHooks();\n} catch (e) {\n  if (/hooks validation failed/.test((e as Error).message)) {\n    // parse per-plugin ✗ lines already printed; fix and re-run\n  } else throw e;\n}","preventionTips":["Validate hook plugins in CI on every change","Copy a known-good plugin skeleton when adding new hooks"],"tags":["cli","hooks","plugin-validation"],"backgroundTag":"plugin-validation-failed","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}