pbakaus/impeccable · warning
invalid catalog: ${validation.errors.join('; ')}
Error message
invalid catalog: ${validation.errors.join('; ')} What it means
Thrown (then swallowed into a null localState) when the local concept catalog at IMPECCABLE_CATALOG_DIR fails schema validation. validateConceptCatalog returns one error string per offending entry and they are joined. The catalog is the curated pool of challenger ingredients; a malformed JSON or a schema-drift between the catalog and this validator makes the whole local catalog unusable.
Source
Thrown at skill/scripts/concept-seed.mjs:137
// network degrades after one timeout total, never one timeout per call.
let apiDeadline = null;
function apiBudgetMs() {
if (apiDeadline === null) apiDeadline = Date.now() + API_TIMEOUT_MS;
return Math.max(0, apiDeadline - Date.now());
}
const localStates = new Map();
function loadLocal(catalogDir = CATALOG_DIR) {
if (localStates.has(catalogDir)) return localStates.get(catalogDir);
let localState;
try {
const catalogState = readConceptCatalog(
join(catalogDir, 'concept-ingredients.json'),
join(catalogDir, 'concept-reviews.json')
);
const validation = validateConceptCatalog(catalogState.catalog, catalogState.reviewData);
if (validation.errors.length > 0) {
throw new Error(`invalid catalog: ${validation.errors.join('; ')}`);
}
const compositionState = readCompositionCatalog(
join(catalogDir, 'composition-ingredients.json'),
join(catalogDir, 'composition-reviews.json')
);
localState = {
concepts: catalogState.concepts,
compositions: compositionState.compositions,
};
} catch {
localState = null;
}
localStates.set(catalogDir, localState);
return localState;
}
function requireLocalConcepts() {
const local = loadLocal();View on GitHub (pinned to d14711ae3d)
Solutions
- Set IMPECCABLE_CATALOG_DIR to a directory with valid catalog JSON, or unset it to fall through to the roll API / degraded seed.
- Read the joined error messages — they name the offending entry id and the violated constraint (e.g. 'review r42 allowedModes may only contain ...').
- Re-run the catalog validator on the files directly: `node -e "import('./skill/scripts/lib/concept-catalog.mjs').then(...)"` to see all errors at once.
- If you edited the schema, migrate existing catalog rows to the new allowed-value sets before running the seeder.
Example fix
// before — catalog has platforms: ["desktop"] which is not allowed // after — use one of COMPOSITION_PLATFORMS "platforms": ["web", "ios"]
Defensive patterns
Strategy: validation
Validate before calling
import { readConceptCatalog, validateConceptCatalog } from './skill/scripts/lib/concept-catalog.mjs';
const state = readConceptCatalog(ingredientsPath, reviewsPath);
const v = validateConceptCatalog(state.catalog, state.reviewData);
if (v.errors.length) console.error(v.errors); // surface before the seeder swallows them Type guard
function isValidCatalog(state) {
return validateConceptCatalog(state.catalog, state.reviewData).errors.length === 0;
} Try / catch
try {
loadLocal(catalogDir);
} catch (err) {
if (/invalid catalog/.test(err.message)) {
console.error('Catalog invalid; fix or unset IMPECCABLE_CATALOG_DIR:', err.message);
} else throw err;
} Prevention
- Run validateConceptCatalog in CI on catalog JSON edits before commit.
- Keep the catalog JSON and the validator schema versioned together; migrate rows when allowed-value sets change.
- Surfacing validation errors directly (rather than the seeder swallowing them) shortens debugging.
When it happens
Trigger: Editing concept-ingredients.json / concept-reviews.json by hand and introducing a field the validator rejects (bad status, strength, grain, platform, or a duplicate id); updating the validator schema in roll-selection.mjs/concept-catalog.mjs without migrating existing catalog entries; a partial git checkout that left a catalog JSON truncated.
Common situations: A contributor adds a new composition with platforms:['desktop'] when only web/ios/android are allowed; a review entry references an allowedMode not in the persuade/operate/read/experience set; the catalog was regenerated by a newer tool than the running seeder.
Related errors
- concept-seed: no local catalog (set IMPECCABLE_CATALOG_DIR o
- concept-seed: --scope must be direction or surface
- concept-seed: --reroll must be a non-negative integer
- concept-seed: --register must be safer or bolder
- concept-seed: --register steers a re-roll round; pass --rero
AI-assisted analysis of pbakaus/impeccable@d14711ae3d (2026-08-13).
Data as JSON: /api/errors/10fc61ba3847b474.
Report an issue: GitHub.