{"record":{"id":"ebe0990d01ac9cf3","repo":"pbakaus/impeccable","slug":"concept-seed-no-local-catalog-set-impeccable-cat-ebe099","errorCode":null,"errorMessage":"concept-seed: no local catalog (set IMPECCABLE_CATALOG_DIR or pass sourceConcepts)","messagePattern":"concept-seed: no local catalog \\(set IMPECCABLE_CATALOG_DIR or pass sourceConcepts\\)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"skill/scripts/concept-seed.mjs","lineNumber":157,"sourceCode":"    const compositionState = readCompositionCatalog(\n      join(catalogDir, 'composition-ingredients.json'),\n      join(catalogDir, 'composition-reviews.json')\n    );\n    localState = {\n      concepts: catalogState.concepts,\n      compositions: compositionState.compositions,\n    };\n  } catch {\n    localState = null;\n  }\n  localStates.set(catalogDir, localState);\n  return localState;\n}\n\nfunction requireLocalConcepts() {\n  const local = loadLocal();\n  if (!local) {\n    throw new Error('concept-seed: no local catalog (set IMPECCABLE_CATALOG_DIR or pass sourceConcepts)');\n  }\n  return local;\n}\n\nasync function fetchRoll({ scope, key, mode, grain, platform, reroll }) {\n  const params = new URLSearchParams({ scope, key, reroll: String(reroll) });\n  if (mode) params.set('mode', mode);\n  if (grain) params.set('grain', grain);\n  if (platform) params.set('platform', platform);\n  const controller = new AbortController();\n  const timer = setTimeout(() => controller.abort(), apiBudgetMs());\n  try {\n    // Race the budget explicitly: abort signals do not reliably cancel the\n    // TCP connect phase, so a blackholed route would otherwise stall ~10s.\n    const response = await Promise.race([\n      fetch(`${API_BASE}/roll?${params}`, { signal: controller.signal }),\n      new Promise(resolveTimeout => setTimeout(() => resolveTimeout(null), apiBudgetMs())),\n    ]);","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/pbakaus/impeccable/blob/d14711ae3d1a1dd62dee61a358d27f107c51ccd0/skill/scripts/concept-seed.mjs#L139-L175","documentation":"Thrown by requireLocalConcepts when loadLocal() returned null, meaning no valid local concept catalog could be loaded. The skill resolves concepts from a local catalog first (the private service repo, evals, and tests point IMPECCABLE_CATALOG_DIR at one), then the roll API, then a degraded assignment-only seed — but requireLocalConcepts demands the local path. The full catalog intentionally does not ship with the skill.","triggerScenarios":"IMPECCABLE_CATALOG_DIR is unset and the script directory has no catalog files; the env var points at an empty or non-existent directory; the catalog files exist but failed validation (see [82]); a fresh public checkout where the private catalog is absent.","commonSituations":"Running concept-seed in a plain skill install (no private catalog); a CI job that forgot to set IMPECCABLE_CATALOG_DIR; pointing the env var at a stale checkout; catalog JSON corrupted after a bad merge.","solutions":["Set IMPECCABLE_CATALOG_DIR to a directory containing valid concept-ingredients.json + concept-reviews.json.","If you are calling seedConcepts programmatically, pass sourceConcepts directly to bypass the local-catalog requirement.","Verify the catalog files exist and pass validation (validateConceptCatalog) at that path.","If you only have API access, use the fetchRoll path instead of requireLocalConcepts."],"exampleFix":"// before\nprocess.env.IMPECCABLE_CATALOG_DIR; // undefined -> throws [83]\nconst local = requireLocalConcepts();\n\n// after: point at a real catalog, or supply concepts inline\nprocess.env.IMPECCABLE_CATALOG_DIR = '/path/to/private/catalog';\n// or, programmatically:\nconst seeded = await seedConcepts({ sourceConcepts: myConcepts, scope: 'surface' });","handlingStrategy":"validation","validationCode":"// Verify a usable local catalog exists before calling requireLocalConcepts.\nimport { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nfunction hasLocalCatalog(dir = process.env.IMPECCABLE_CATALOG_DIR) {\n  if (!dir) return false;\n  return existsSync(join(dir, 'concept-ingredients.json'))\n    && existsSync(join(dir, 'concept-reviews.json'));\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Set IMPECCABLE_CATALOG_DIR in CI/eval environments that need the local path.","When calling seedConcepts programmatically, pass sourceConcepts to avoid the local-catalog dependency entirely.","If you only have network access, use the fetchRoll path instead of requireLocalConcepts."],"tags":["catalog","configuration","env","concept-seed"],"backgroundTag":null,"analyzedSha":"d14711ae3d1a1dd62dee61a358d27f107c51ccd0","analyzedAt":"2026-08-13T00:52:25.771Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}