Yeachan-Heo/oh-my-codex · error · Error
catalog_manifest_invalid:skills[${index}]
Error message
catalog_manifest_invalid:skills[${index}] What it means
Each element of the skills array must be a plain JSON object; this error names the index of the first entry that is not (a string, number, null, or array). It fires during the per-entry mapping loop in validateCatalogManifest.
Source
Thrown at src/catalog/schema.ts:57
throw new Error(`catalog_manifest_invalid:${field}`);
}
}
export function validateCatalogManifest(input: unknown): CatalogManifest {
if (!isObject(input)) throw new Error('catalog_manifest_invalid:root');
if (typeof input.schemaVersion !== 'number' || !Number.isInteger(input.schemaVersion)) {
throw new Error('catalog_manifest_invalid:schemaVersion');
}
assertNonEmptyString(input.catalogVersion, 'catalogVersion');
if (!Array.isArray(input.skills)) throw new Error('catalog_manifest_invalid:skills');
if (!Array.isArray(input.agents)) throw new Error('catalog_manifest_invalid:agents');
const seenSkills = new Set<string>();
const skills: CatalogSkillEntry[] = input.skills.map((entry, index) => {
if (!isObject(entry)) throw new Error(`catalog_manifest_invalid:skills[${index}]`);
assertNonEmptyString(entry.name, `skills[${index}].name`);
assertNonEmptyString(entry.category, `skills[${index}].category`);
assertNonEmptyString(entry.status, `skills[${index}].status`);
if (!SKILL_CATEGORIES.has(entry.category as CatalogSkillCategory)) {
throw new Error(`catalog_manifest_invalid:skills[${index}].category`);
}
if (!ENTRY_STATUSES.has(entry.status as CatalogEntryStatus)) {
throw new Error(`catalog_manifest_invalid:skills[${index}].status`);
}
const name = entry.name.trim();
if (seenSkills.has(name)) throw new Error(`catalog_manifest_invalid:duplicate_skill:${name}`);
seenSkills.add(name);
const canonical = typeof entry.canonical === 'string' && entry.canonical.trim() !== ''
? entry.canonical.trim()
: undefined;View on GitHub (pinned to 3ad79a8a6f)
Solutions
- Replace the offending element with a full entry object {name, category, status}
- Regenerate the manifest
- Use the JSON schema/tooling rather than editing arrays by hand
Example fix
// before
"skills": ["autopilot"]
// after
"skills": [ { "name": "autopilot", "category": "execution", "status": "active" } ] Defensive patterns
Strategy: validation
Validate before calling
if (!raw.skills.every((s: unknown) => typeof s === 'object' && s !== null && !Array.isArray(s))) { /* fix entries */ } Type guard
const isEntry = (v: unknown): v is Record<string, unknown> => typeof v === 'object' && v !== null && !Array.isArray(v);
Prevention
- Never use shorthand string entries in skills
- Schema-check the manifest during build
When it happens
Trigger: A skills array containing a bare string like "autopilot" or a null at the reported index.
Common situations: Shorthand manifests listing skill names as strings, or trailing commas/nulls introduced by manual edits.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- catalog_manifest_invalid:skills[${index}].category
- catalog_manifest_invalid:skills[${index}].status
- catalog_manifest_invalid:duplicate_skill:${name}
- catalog_manifest_invalid:skills[${index}].canonical
- catalog_manifest_invalid:${field}
AI-assisted analysis of Yeachan-Heo/oh-my-codex@3ad79a8a6f (2026-08-27).
Data as JSON: /api/errors/0a75db64305c22b6.
Report an issue: GitHub.