JuliusBrussee/caveman · error
caveman build: unresolved relative source import ${JSON.stri
Error message
caveman build: unresolved relative source import ${JSON.stringify(specifier)} What it means
A relative import's candidate files (the specifier plus extension/index variants tried by the resolver) all returned ENOENT, so the source graph cannot find the imported module. The build requires a complete, finite file closure; an unresolved relative import makes the graph non-lockable and aborts with the failing specifier in the message.
Source
Thrown at packages/agent/src/source-graph.ts:84
const candidates = extname(base)
? explicitImportCandidates(base, path)
: [
base,
...RESOLUTION_EXTENSIONS.map((extension) => base + extension),
...SOURCE_EXTENSIONS.map((extension) => resolve(base, `index${extension}`)),
];
let found: string | undefined;
for (const candidate of candidates) {
try {
await readFile(candidate);
found = candidate;
break;
} catch (error) {
if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
}
}
if (!found) {
throw new Error(`caveman build: unresolved relative source import ${JSON.stringify(specifier)}`);
}
if (bare) {
await collectPackageClosure(packageRoot!, files, packageRoots);
continue;
}
const lexicalFound = found;
found = await realpath(found);
const insideRoot = isPathWithin(canonicalRoot, found);
if (!insideRoot && !bare && !external.has(path)) {
const lexicalInsideRoot = isPathWithin(canonicalRoot, lexicalFound);
if (lexicalInsideRoot) {
throw new Error("caveman build: source graph symlink escapes project root");
}
throw new Error("caveman build: imported source escapes project root");
}
if (!insideRoot) external.add(found);
if (!files.has(found)) {
files.add(found);View on GitHub (pinned to 27d5a3981a)
Solutions
- Check the specifier in the error message and create/rename the target file so one of the standard candidates exists (exact path, .ts/.js/.tsx variants, /index.*).
- Run the TypeScript compiler to surface the same broken import in your editor before building.
- On Linux, verify path casing matches the import exactly.
Example fix
// before: src/a.ts
import { x } from "./b/c"; // src/b/c.ts was moved to src/b/c/index.ts? or deleted
// after: restore the file at src/b/c.ts (or update the import to the real path) Defensive patterns
Strategy: validation
Validate before calling
import { access } from "node:fs/promises";
async function relativeImportResolves(from: string, spec: string): Promise<boolean> {
const base = resolve(dirname(from), spec);
const candidates = extname(base)
? [base]
: [base, `${base}.ts`, `${base}.tsx`, `${base}.js`, `${base}.mjs`, `${base}.cjs`, `${base}/index.ts`, `${base}/index.js`];
return access(candidates[0]!).then(() => true, () => false); // check each candidate similarly
} Try / catch
try {
await buildSourceGraph(root);
} catch (error) {
if (error instanceof Error && error.message.startsWith("caveman build: unresolved relative source import")) {
// the failing specifier is quoted in the message; create/rename the file or fix the path
} else throw error;
} Prevention
- Keep TypeScript strict so broken relative imports fail in the editor first.
- After moving/renaming files, run a project-wide import check before building.
- Match path casing exactly on case-sensitive filesystems.
When it happens
Trigger: A source file imports "./utils/helper" when the file is helper.ts in a directory the candidate list does not cover, the file was renamed/deleted, or the specifier has a casing mismatch on case-sensitive filesystems.
Common situations: Renaming or moving a module without updating importers; case mismatches after cloning onto Linux from macOS/Windows; a missing file that TypeScript's own resolution tolerates via different fallback rules than this builder.
Related errors
- caveman build: unresolved source dependency ${JSON.stringify
- caveman build: imported source escapes project root
- package not found
- invalid package
- caveman build: dataResidency is not enforced yet; refusing t
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/4a48d96736824051.
Report an issue: GitHub.