JuliusBrussee/caveman · error
caveman build: config must use strict lock and required…
Error message
caveman build: config must use strict lock and required sandbox
What it means
loadBuildInputs imports the caveman config and enforces that every build uses lock: "strict" and sandbox: "required". These are non-negotiable safety invariants of the caveman build system — a config relaxing either is rejected before any agent loading happens. The error fires when the config is missing entirely, or when either field has a different value.
Solutions
- Set `lock: "strict"` and `sandbox: "required"` in the defineBuild() call in caveman.config.ts.
- If you copied an old config, update it to the current schema and re-run.
- Ensure the config actually exports via default or `config` so the object is seen.
- Re-run `npm run build` after fixing.
Example fix
// before
export default defineBuild({ lock: "loose", sandbox: "optional", evals: "evals/*.eval.ts", entry: "src/agent.ts" });
// after
export default defineBuild({ lock: "strict", sandbox: "required", evals: "evals/*.eval.ts", entry: "src/agent.ts" }); Defensive patterns
Strategy: validation
Validate before calling
const cfg = (await import("./caveman.config.ts")).default;
if (!cfg || cfg.lock !== "strict" || cfg.sandbox !== "required") {
throw new Error("caveman.config.ts must set lock: 'strict' and sandbox: 'required'");
} Type guard
const isStrictConfig = (c: unknown): c is BuildConfig & { lock: "strict"; sandbox: "required" } =>
typeof c === "object" && c !== null &&
(c as BuildConfig).lock === "strict" && (c as BuildConfig).sandbox === "required"; Try / catch
try {
await loadBuildInputs(root, configPath);
} catch (err) {
if (String(err?.message).includes("strict lock and required sandbox")) {
console.error("Update caveman.config.ts: defineBuild({ lock: 'strict', sandbox: 'required', ... })");
}
throw err;
} Prevention
- Start every new config from the official template that already sets strict/required.
- Never disable the sandbox even for local debugging — use a fixture instead.
- Type configs with BuildConfig and literal types so invalid values fail at compile time.
- Add a repo-level test asserting all configs are strict/required.
When it happens
Trigger: caveman.config.ts omits `lock` or `sandbox`, sets `lock: "loose"|"off"` or `sandbox: "optional"|"disabled"`, exports a non-object, or uses an old config shape without these fields.
Common situations: Following an outdated tutorial/template that set sandbox to optional; disabling the sandbox locally for debugging and forgetting to revert; hand-written config missing the fields; defineBuild defaults that don't satisfy the strict requirement.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- auth_token: in is ignored — the inbound token is read only…
- cave_breaker_retry_backoff_invalid
- cave_breaker_retry_requires_budget
- cave_breaker_retry_spend_invalid
- cave_breaker_threshold_invalid
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/52afed0fe8d9aa61.
Report an issue: GitHub.
Appendix: source
Thrown at packages/agent/src/cli.ts:1005
"package.json",
"package-lock.json",
"npm-shrinkwrap.json",
"pnpm-lock.yaml",
"yarn.lock",
] as const;
async function loadBuildInputs(
root: string,
configPath: string,
beforeSourceHash?: (
inputs: Omit<LoadedBuildInputs, "sourceSha256">,
) => Promise<void>,
): Promise<LoadedBuildInputs> {
const configAbsolute = resolve(root, configPath);
const imported = await importFresh(configAbsolute) as { default?: BuildConfig; config?: BuildConfig };
const config = imported.default ?? imported.config;
if (!config || config.lock !== "strict" || config.sandbox !== "required") {
throw new Error("caveman build: config must use strict lock and required sandbox");
}
const entryAbsolute = resolve(root, config.entry);
const agent = await loadAgent(entryAbsolute);
const evalFiles: string[] = [];
for await (const path of glob(config.evals, { cwd: root })) evalFiles.push(resolve(root, path));
evalFiles.sort();
const evals: EvalDefinition[] = [];
for (const path of evalFiles) {
const module = await importFresh(path) as Record<string, unknown>;
for (const value of Object.values(module)) {
if (isEval(value)) evals.push(value);
}
}
await beforeSourceHash?.({ config, agent, evals });
const sourceFiles = new Set<string>([configAbsolute, entryAbsolute, ...evalFiles]);
for (const pattern of SOURCE_PATTERNS) {
for await (const path of glob(pattern, { cwd: root })) sourceFiles.add(resolve(root, path));
}View on GitHub (pinned to 3ee70a1026)