{"record":{"id":"d1965be99faa2465","repo":"Yeachan-Heo/oh-my-codex","slug":"catalog-manifest-missing","errorCode":null,"errorMessage":"catalog_manifest_missing","messagePattern":"catalog_manifest_missing","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/catalog/reader.ts","lineNumber":26,"sourceCode":"  ['src', 'catalog', 'manifest.json'],\n  ['dist', 'catalog', 'manifest.json'],\n] as const;\n\nlet cachedManifest: CatalogManifest | null = null;\nlet cachedPath: string | null = null;\n\nfunction resolveManifestPath(packageRoot: string): string | null {\n  for (const segments of MANIFEST_CANDIDATE_PATHS) {\n    const fullPath = join(packageRoot, ...segments);\n    if (existsSync(fullPath)) return fullPath;\n  }\n  return null;\n}\n\nexport function readCatalogManifest(packageRoot: string = getPackageRoot()): CatalogManifest {\n  const path = resolveManifestPath(packageRoot);\n  if (!path) {\n    throw new Error('catalog_manifest_missing');\n  }\n\n  if (cachedManifest && cachedPath === path) return cachedManifest;\n\n  const raw = JSON.parse(readFileSync(path, 'utf8')) as unknown;\n  const manifest = validateCatalogManifest(raw);\n  cachedManifest = manifest;\n  cachedPath = path;\n  return manifest;\n}\n\nexport function tryReadCatalogManifest(packageRoot: string = getPackageRoot()): CatalogManifest | null {\n  try {\n    return readCatalogManifest(packageRoot);\n  } catch {\n    return null;\n  }\n}","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/catalog/reader.ts#L8-L44","documentation":"readCatalogManifest locates the catalog manifest by probing templates/catalog-manifest.json, src/catalog/manifest.json, and dist/catalog/manifest.json under the package root; if none exists it throws catalog_manifest_missing. This protects callers from silently operating without a catalog.","triggerScenarios":"Calling readCatalogManifest(packageRoot) (directly or via getCatalogCounts, contract, installableAgentNames) when the package root has none of the three candidate manifest files — typical for an incompletely built package or a wrong packageRoot argument.","commonSituations":"Running from a checkout before generating the manifest, a dist/ build that pruned catalog files, passing a custom packageRoot that doesn't contain the manifest, or packaging rules excluding JSON files.","solutions":["Verify which path exists: templates/catalog-manifest.json or src/catalog/manifest.json or dist/catalog/manifest.json under the root you pass","If dist is incomplete, rebuild the package so dist/catalog/manifest.json is emitted","If you pass packageRoot explicitly, point it at the actual package root containing the manifest","Use tryReadCatalogManifest() if a null return is acceptable in your flow"],"exampleFix":"// before\nconst manifest = readCatalogManifest(someDir); // throws\n\n// after\nimport { tryReadCatalogManifest } from './catalog/reader.js';\nconst manifest = tryReadCatalogManifest(someDir) ?? fallbackManifest;","handlingStrategy":"fallback","validationCode":"import { tryReadCatalogManifest } from './catalog/reader.js';\nconst manifest = tryReadCatalogManifest(root);\nif (!manifest) { /* use fallback catalog or fail gracefully */ }","typeGuard":null,"tryCatchPattern":"try { readCatalogManifest(root); } catch (e) { if ((e as Error).message === 'catalog_manifest_missing') { /* fallback */ } throw e; }","preventionTips":["Ensure templates/ or dist/catalog/manifest.json ships with the package","Prefer tryReadCatalogManifest for optional reads","Verify custom packageRoot arguments in tests"],"tags":["catalog","manifest","file-not-found","packaging"],"backgroundTag":"missing-required-file","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}