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

  1. Rewrite absolute specifiers as real relative imports (./ or ../) from each importing file.
  2. Remove reliance on TS `baseUrl`/`paths` non-relative mappings for files that will enter the locked source graph.
  3. 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

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


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/abfe6caadc727c5b. Report an issue: GitHub.