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

  1. Set IMPECCABLE_CATALOG_DIR to a directory with valid catalog JSON, or unset it to fall through to the roll API / degraded seed.
  2. Read the joined error messages — they name the offending entry id and the violated constraint (e.g. 'review r42 allowedModes may only contain ...').
  3. 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.
  4. 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

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


AI-assisted analysis of pbakaus/impeccable@d14711ae3d (2026-08-13). Data as JSON: /api/errors/10fc61ba3847b474. Report an issue: GitHub.