{"record":{"id":"b326c481c70380ee","repo":"Yeachan-Heo/oh-my-codex","slug":"catalog-manifest-invalid-skills","errorCode":null,"errorMessage":"catalog_manifest_invalid:skills","messagePattern":"catalog_manifest_invalid:skills","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/catalog/schema.ts","lineNumber":52,"sourceCode":"  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}]`);\n    assertNonEmptyString(entry.name, `skills[${index}].name`);\n    assertNonEmptyString(entry.category, `skills[${index}].category`);\n    assertNonEmptyString(entry.status, `skills[${index}].status`);\n\n    if (!SKILL_CATEGORIES.has(entry.category as CatalogSkillCategory)) {\n      throw new Error(`catalog_manifest_invalid:skills[${index}].category`);\n    }\n    if (!ENTRY_STATUSES.has(entry.status as CatalogEntryStatus)) {\n      throw new Error(`catalog_manifest_invalid:skills[${index}].status`);\n    }\n\n    const name = entry.name.trim();\n    if (seenSkills.has(name)) throw new Error(`catalog_manifest_invalid:duplicate_skill:${name}`);","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/catalog/schema.ts#L34-L70","documentation":"validateCatalogManifest requires the 'skills' key to be an array of skill entries. This error means 'skills' is missing, an object, or a scalar. Note that an empty array passes this check; per-entry problems surface as skills[i] errors.","triggerScenarios":"A manifest JSON where \"skills\" is absent, {}, or a string; e.g. someone keyed skills by name instead of listing them.","commonSituations":"Restructuring the manifest into a map for convenience, or generators that omit skills entirely in agent-only catalogs without emitting [].","solutions":["Change skills to an array of entry objects (use [] if there are none)","Regenerate the manifest from canonical source data","Add validateCatalogManifest to CI to catch regressions"],"exampleFix":"// before\n\"skills\": { \"autopilot\": { \"category\": \"execution\" } }\n\n// after\n\"skills\": [ { \"name\": \"autopilot\", \"category\": \"execution\", \"status\": \"active\" } ]","handlingStrategy":"validation","validationCode":"if (!Array.isArray(raw.skills)) { /* reject or coerce to array before validation */ }","typeGuard":"function hasSkillsArray(v: unknown): v is { skills: unknown[] } { return typeof v === 'object' && v !== null && Array.isArray((v as any).skills); }","tryCatchPattern":null,"preventionTips":["Keep skills a list, not a name-keyed map","Use generator tooling to emit the manifest"],"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"}