JuliusBrussee/caveman · error
invalid package
Error message
invalid package
What it means
barePackageName() throws "invalid package" when the specifier's first path segment is empty — concretely, specifiers starting with "/" (absolute paths). The source graph treats leading-"/" imports as malformed bare specifiers rather than resolvable paths; imports must be relative (./ or ../) or bare package names.
Source
Thrown at packages/agent/src/source-graph.ts:214
if (entry.name === "node_modules" || entry.name === ".git") continue;
const path = resolve(directory, entry.name);
if (entry.isDirectory()) {
await collectPackageFiles(path, files);
} else if (entry.isFile()) {
files.add(path);
} else if (entry.isSymbolicLink()) {
throw new Error(`caveman build: package artifact symlink is not lockable: ${JSON.stringify(path)}`);
}
}
}
function barePackageName(specifier: string): string {
const parts = specifier.split("/");
if (specifier.startsWith("@")) {
if (parts.length < 2 || parts[1] === "") throw new Error("invalid scoped package");
return `${parts[0]}/${parts[1]}`;
}
if (parts[0] === "") throw new Error("invalid package");
return parts[0]!;
}
function resolvePackageExport(exportsValue: unknown, subpath: string): string | undefined {
if (typeof exportsValue === "string" || Array.isArray(exportsValue)) {
return subpath === "." ? resolveConditionalExport(exportsValue) : undefined;
}
if (!isRecord(exportsValue)) return undefined;
const keys = Object.keys(exportsValue);
if (!keys.some((key) => key.startsWith("."))) {
return subpath === "." ? resolveConditionalExport(exportsValue) : undefined;
}
if (Object.hasOwn(exportsValue, subpath)) {
return resolveConditionalExport(exportsValue[subpath]);
}
const patterns = keys
.filter((key) => key.startsWith("./") && key.includes("*"))
.sort((left, right) => right.length - left.length);View on GitHub (pinned to 27d5a3981a)
Solutions
- Rewrite absolute specifiers as real relative imports (./ or ../) from each importing file.
- Remove reliance on TS `baseUrl`/`paths` non-relative mappings for files that will enter the locked source graph.
- If you need path aliases, resolve them to relative specifiers in a pre-build step.
Example fix
// before
import { helper } from "/src/lib/helper";
// after
import { helper } from "./lib/helper"; Defensive patterns
Strategy: type-guard
Validate before calling
function isLockableSpecifier(spec: string): boolean {
return spec.startsWith("./") || spec.startsWith("../") || !spec.startsWith("/");
}
if (!isLockableSpecifier(spec)) throw new Error(`non-lockable specifier: ${spec}`); Type guard
const isImportableSpecifier = (spec: string): boolean =>
!spec.startsWith("/"); // reject absolute-path specifiers Prevention
- Avoid TS baseUrl-style absolute imports in code destined for the locked graph.
- Configure an eslint rule (import/no-absolute-path) to catch "/"-prefixed imports.
- Prefer relative or package specifiers exclusively.
When it happens
Trigger: A source file contains import "/src/lib/foo" — an absolute filesystem path used as a module specifier.
Common situations: Configuring a bundler/TS `baseUrl` or `paths` style that allows absolute-looking specifiers, then building with this framework which has no path-mapping support; porting code from bundlers that resolve /-prefixed imports.
Related errors
- caveman build: unresolved relative source import ${JSON.stri
- caveman build: imported source escapes project root
- invalid scoped package
- caveman build: dataResidency is not enforced yet; refusing t
- caveman build: requiredFixturePassRate must be in (0,1]
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/abfe6caadc727c5b.
Report an issue: GitHub.