JuliusBrussee/caveman · error

caveman build: unresolved source dependency ${JSON.stringify

Error message

caveman build: unresolved source dependency ${JSON.stringify(specifier)}

What it means

While building the source graph, a bare (package) import specifier could be resolved neither by Node's createRequire resolution nor by the ESM import-only fallback. The build lock must hash every reachable file of every dependency, so an unresolvable bare import aborts the build with the failing specifier embedded in the message (and the require error as cause).

Source

Thrown at packages/agent/src/source-graph.ts:57

      ...esmSourceSpecifiers(typescript.lexableSource, path),
      ...typescript.specifiers,
      ...legacySourceSpecifiers(source, path, code),
    ]);
    for (const specifier of specifiers) {
      if (BUILTINS.has(specifier)) continue;
      const bare = !specifier.startsWith(".");
      if (bare && !includeBareDependencies) continue;
      let base: string;
      let packageRoot: string | undefined;
      if (bare) {
        packageRoot = dependencyPackageRoot(specifier, path);
        try {
          base = createRequire(path).resolve(specifier);
        } catch (requireError) {
          try {
            base = await resolveImportOnlyDependency(specifier, path);
          } catch {
            throw new Error(
              `caveman build: unresolved source dependency ${JSON.stringify(specifier)}`,
              { cause: requireError },
            );
          }
        }
      } else {
        base = resolve(dirname(path), specifier);
      }
      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 {

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Run the package manager install (pnpm install / npm install) so the specifier resolves from the importing file.
  2. Check the exact specifier in the error message against package.json dependencies — typos and missing subpath exports are the most common cause.
  3. If the import is conditional/optional, guard it behind a resolvable path or declare the dependency properly instead of relying on hoisting.

Example fix

// before: package.json lacks the dep, source has:
import { foo } from "some-dep/sub";

// after: add the dependency
// pnpm add some-dep   (and verify "some-dep/sub" is in its exports map)
Defensive patterns

Strategy: validation

Validate before calling

import { createRequire } from "node:module";
async function bareImportResolves(specifier: string, importer: string): Promise<boolean> {
  try {
    createRequire(importer).resolve(specifier);
    return true;
  } catch {
    try {
      await import(/* specifier probe */ specifier); // or use import.meta.resolve when available
      return true;
    } catch { return false; }
  }
}

Try / catch

try {
  await buildSourceGraph(root);
} catch (error) {
  if (error instanceof Error && error.message.startsWith("caveman build: unresolved source dependency")) {
    // specifier is in the message; run install or fix the import
  } else throw error;
}

Prevention

When it happens

Trigger: A project source file imports a package that is not installed, is installed in a layout Node cannot resolve from that importer, or exposes no matching entry in its exports map for the importer's conditions.

Common situations: Missing dependency in package.json after cloning (no install), a typo'd package name, an import of a subpath the package's exports map does not expose, or a pnpm hoisting layout where the package is not declared as a direct dependency.

Related errors


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