{"record":{"id":"620178eb66e7074c","repo":"JuliusBrussee/caveman","slug":"caveman-build-config-must-export-definebuild","errorCode":null,"errorMessage":"caveman build: config must export defineBuild()","messagePattern":"caveman build: config must export defineBuild\\(\\)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/agent/src/cli.ts","lineNumber":676,"sourceCode":"  loaded: Awaited<ReturnType<typeof loadDevModule>>,\n): Promise<boolean> {\n  const lockPath = \".caveman/agent.lock.json\";\n  try {\n    await readFile(resolve(root, lockPath));\n  } catch (error) {\n    if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return false;\n    throw error;\n  }\n  const configPath = \"caveman.config.ts\";\n  await loaded.includeFiles([lockPath]);\n  await loaded.includeSourceGraph([configPath]);\n  const imported = await importFresh(resolve(loaded.rootDir, configPath)) as {\n    default?: BuildConfig;\n    config?: BuildConfig;\n  };\n  const config = imported.default ?? imported.config;\n  if (!config || typeof config.evals !== \"string\") {\n    throw new Error(\"caveman build: config must export defineBuild()\");\n  }\n  const evalFiles: string[] = [];\n  for await (const path of glob(config.evals, { cwd: root })) {\n    evalFiles.push(resolve(root, path));\n  }\n  await loaded.includeSourceGraph(evalFiles);\n  const sourceFiles: string[] = [];\n  for (const pattern of SOURCE_PATTERNS) {\n    for await (const path of glob(pattern, { cwd: root })) sourceFiles.push(resolve(root, path));\n  }\n  await loaded.includeFiles(sourceFiles);\n  await loaded.includeOptionalFiles(PACKAGE_STATE_FILES);\n  return true;\n}\n\nfunction firstUsefulError(error: unknown): string {\n  const stack = error instanceof Error ? error.stack ?? error.message : String(error);\n  return stack.split(\"\\n\").slice(0, 4).join(\"\\n\");","sourceCodeStart":658,"sourceCodeEnd":694,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/agent/src/cli.ts#L658-L694","documentation":"During build, the CLI fresh-imports caveman.config.ts and expects a default or named `config` export shaped by defineBuild(); the sanity check requires config to be truthy and config.evals to be a string. Failing either means the module didn't export a defineBuild() result (default export missing, wrong export name, or the file exports something else), and the throw names the expected export.","triggerScenarios":"caveman.config.ts with `export const config = defineBuild({...})` where evals is missing/not a string; exporting under a different name (`export const myConfig`); forgetting to call defineBuild and exporting a plain object without an `evals: string` glob; syntax that makes importFresh resolve to an empty module namespace.","commonSituations":"First-time config authoring; refactoring the config and dropping the `export default`; copy-pasting a config example that predates defineBuild; circular import making the default undefined at evaluation time.","solutions":["Write the config as: import { defineBuild } from \"…\"; export default defineBuild({ evals: \"evals/**/*.ts\", … });","Ensure the `evals` field is a string glob — it is the one field checked, so omitting it triggers the same error as no export.","Confirm the file is exactly caveman.config.ts at the repo root (the loader imports that fixed path)."],"exampleFix":"// before: caveman.config.ts\nexport const cavemanConfig = { evals: \"evals/**\" };\n\n// after\nimport { defineBuild } from \"@caveman/agent\";\nexport default defineBuild({ evals: \"evals/**/*.ts\" });","handlingStrategy":"type-guard","validationCode":"import { pathToFileURL } from \"node:url\";\nasync function configExportsDefineBuild(root: string): Promise<boolean> {\n  const mod = await import(`${pathToFileURL(join(root, \"caveman.config.ts\")).href}?t=${Date.now()}`);\n  const config = mod.default ?? mod.config;\n  return Boolean(config) && typeof config.evals === \"string\";\n}","typeGuard":"interface BuildConfigShape { evals: string }\nfunction isBuildConfig(value: unknown): value is BuildConfigShape {\n  return typeof value === \"object\" && value !== null\n    && typeof (value as { evals?: unknown }).evals === \"string\";\n}","tryCatchPattern":null,"preventionTips":["Always author caveman.config.ts as `export default defineBuild({ evals: \"…\", … })`.","Keep `evals` a string glob — it is the field the loader sanity-checks.","Avoid circular imports in the config file; they can make the default export undefined at import time."],"tags":["configuration","build","exports","typescript"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}