JuliusBrussee/caveman · error · Error
caveman build: config must export defineBuild()
Error message
caveman build: config must export defineBuild()
What it means
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.
Source
Thrown at packages/agent/src/cli.ts:676
loaded: Awaited<ReturnType<typeof loadDevModule>>,
): Promise<boolean> {
const lockPath = ".caveman/agent.lock.json";
try {
await readFile(resolve(root, lockPath));
} catch (error) {
if ((error as NodeJS.ErrnoException).code === "ENOENT") return false;
throw error;
}
const configPath = "caveman.config.ts";
await loaded.includeFiles([lockPath]);
await loaded.includeSourceGraph([configPath]);
const imported = await importFresh(resolve(loaded.rootDir, configPath)) as {
default?: BuildConfig;
config?: BuildConfig;
};
const config = imported.default ?? imported.config;
if (!config || typeof config.evals !== "string") {
throw new Error("caveman build: config must export defineBuild()");
}
const evalFiles: string[] = [];
for await (const path of glob(config.evals, { cwd: root })) {
evalFiles.push(resolve(root, path));
}
await loaded.includeSourceGraph(evalFiles);
const sourceFiles: string[] = [];
for (const pattern of SOURCE_PATTERNS) {
for await (const path of glob(pattern, { cwd: root })) sourceFiles.push(resolve(root, path));
}
await loaded.includeFiles(sourceFiles);
await loaded.includeOptionalFiles(PACKAGE_STATE_FILES);
return true;
}
function firstUsefulError(error: unknown): string {
const stack = error instanceof Error ? error.stack ?? error.message : String(error);
return stack.split("\n").slice(0, 4).join("\n");View on GitHub (pinned to 27d5a3981a)
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).
Example fix
// before: caveman.config.ts
export const cavemanConfig = { evals: "evals/**" };
// after
import { defineBuild } from "@caveman/agent";
export default defineBuild({ evals: "evals/**/*.ts" }); Defensive patterns
Strategy: type-guard
Validate before calling
import { pathToFileURL } from "node:url";
async function configExportsDefineBuild(root: string): Promise<boolean> {
const mod = await import(`${pathToFileURL(join(root, "caveman.config.ts")).href}?t=${Date.now()}`);
const config = mod.default ?? mod.config;
return Boolean(config) && typeof config.evals === "string";
} Type guard
interface BuildConfigShape { evals: string }
function isBuildConfig(value: unknown): value is BuildConfigShape {
return typeof value === "object" && value !== null
&& typeof (value as { evals?: unknown }).evals === "string";
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- cave_breaker_threshold_invalid
- caveman agent: subagent maxCalls must be a positive integer
- cave_vercel_terminal_failure
- cave_breaker_retry_requires_budget
- cave_breaker_retry_spend_invalid
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/620178eb66e7074c.
Report an issue: GitHub.