{"record":{"id":"359d6f1ca3445773","repo":"Yeachan-Heo/oh-my-codex","slug":"catalog-manifest-invalid-root","errorCode":null,"errorMessage":"catalog_manifest_invalid:root","messagePattern":"catalog_manifest_invalid:root","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/catalog/schema.ts","lineNumber":44,"sourceCode":"}\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}]`);\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)) {","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/catalog/schema.ts#L26-L62","documentation":"validateCatalogManifest requires the parsed manifest JSON to be a plain object at the top level; catalog_manifest_invalid:root means the input was an array, a primitive, or null. It fires before any field checks and indicates the file is structurally not a manifest at all.","triggerScenarios":"readCatalogManifest loading a manifest file whose JSON root is an array (e.g. a list of entries), a bare string/number, or 'null'.","commonSituations":"Manifest file replaced with a different JSON document, a truncated write leaving invalid content that still parses, or pointing resolveManifestPath at the wrong file.","solutions":["Open the resolved manifest path and confirm the top level is { ... } with schemaVersion, catalogVersion, skills, agents","If the file is wrong/truncated, regenerate or restore it from source control","Check that only one of templates/src/dist manifest files exists and it is the intended one"],"exampleFix":"// before\n[{ \"name\": \"autopilot\" }]\n\n// after\n{ \"schemaVersion\": 1, \"catalogVersion\": \"1.0.0\", \"skills\": [...], \"agents\": [...] }","handlingStrategy":"validation","validationCode":"const raw: unknown = JSON.parse(text);\nif (typeof raw !== 'object' || raw === null || Array.isArray(raw)) { /* reject before calling reader */ }","typeGuard":"function isPlainObject(v: unknown): v is Record<string, unknown> { return typeof v === 'object' && v !== null && !Array.isArray(v); }","tryCatchPattern":null,"preventionTips":["Lint manifest JSON in editors with schema support","Never store the manifest as a top-level array"],"tags":["catalog","manifest","json","validation"],"backgroundTag":"schema-validation-failed","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}