{"record":{"id":"214eb775129b1cca","repo":"Yeachan-Heo/oh-my-codex","slug":"catalog-manifest-invalid-field","errorCode":null,"errorMessage":"catalog_manifest_invalid:${field}","messagePattern":"catalog_manifest_invalid:(.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/catalog/schema.ts","lineNumber":39,"sourceCode":"export interface CatalogManifest {\n  schemaVersion: number;\n  catalogVersion: string;\n  skills: CatalogSkillEntry[];\n  agents: CatalogAgentEntry[];\n}\n\nconst SKILL_CATEGORIES = new Set<CatalogSkillCategory>(['execution', 'planning', 'shortcut', 'utility']);\nconst AGENT_CATEGORIES = new Set<CatalogAgentCategory>(['build', 'review', 'domain', 'product', 'coordination']);\nconst ENTRY_STATUSES = new Set<CatalogEntryStatus>(['active', 'alias', 'merged', 'deprecated', 'internal']);\nconst REQUIRED_CORE_SKILLS = new Set(['autopilot', 'ralplan', 'team', 'ultragoal']);\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n  return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction assertNonEmptyString(value: unknown, field: string): asserts value is string {\n  if (typeof value !== 'string' || value.trim() === '') {\n    throw new Error(`catalog_manifest_invalid:${field}`);\n  }\n}\n\nexport function validateCatalogManifest(input: unknown): CatalogManifest {\n  if (!isObject(input)) throw new Error('catalog_manifest_invalid:root');\n\n  if (typeof input.schemaVersion !== 'number' || !Number.isInteger(input.schemaVersion)) {\n    throw new Error('catalog_manifest_invalid:schemaVersion');\n  }\n\n  assertNonEmptyString(input.catalogVersion, 'catalogVersion');\n\n  if (!Array.isArray(input.skills)) throw new Error('catalog_manifest_invalid:skills');\n  if (!Array.isArray(input.agents)) throw new Error('catalog_manifest_invalid:agents');\n\n  const seenSkills = new Set<string>();\n  const skills: CatalogSkillEntry[] = input.skills.map((entry, index) => {\n    if (!isObject(entry)) throw new Error(`catalog_manifest_invalid:skills[${index}]`);","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/catalog/schema.ts#L21-L57","documentation":"This is the generic field-level validation failure from assertNonEmptyString inside validateCatalogManifest: the named field is missing, not a string, or whitespace-only. The message embeds the offending field path (e.g. skills[2].name, catalogVersion). It ensures every catalog manifest string field is a usable non-empty value.","triggerScenarios":"A catalog manifest JSON where any string field validated via assertNonEmptyString (catalogVersion, skills[i].name/category/status, agents[i].name/category/status) is null, a number, an empty string, or whitespace.","commonSituations":"Hand-editing catalog-manifest.json and leaving a field blank, templating bugs that emit empty placeholders, or manifest generators skipping fields for some entries.","solutions":["Read the field path in the message and open the manifest at that entry","Set the field to a non-empty string (correct category/status values per the schema)","Regenerate the manifest with the official generator rather than editing by hand","Add a CI check that runs validateCatalogManifest on the built manifest"],"exampleFix":"// before\n{ \"name\": \"  \", \"category\": \"execution\", \"status\": \"active\" }\n\n// after\n{ \"name\": \"autopilot\", \"category\": \"execution\", \"status\": \"active\" }","handlingStrategy":"validation","validationCode":"import { validateCatalogManifest } from './catalog/schema.js';\nvalidateCatalogManifest(JSON.parse(readFileSync(path, 'utf8'))); // catch before shipping","typeGuard":"function isNonEmptyString(v: unknown): v is string { return typeof v === 'string' && v.trim() !== ''; }","tryCatchPattern":"try { readCatalogManifest(root); } catch (e) { if ((e as Error).message.startsWith('catalog_manifest_invalid:')) { /* report field path, fix manifest */ } throw e; }","preventionTips":["Run validateCatalogManifest in CI against built manifests","Generate manifests with tooling, not by hand"],"tags":["catalog","manifest","validation"],"backgroundTag":"schema-validation-failed","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}