JuliusBrussee/caveman · error

caveman build: aliased source loader is not lockable in

Error message

caveman build: aliased source loader is not lockable in ${JSON.stringify(path)}

What it means

During build, the caveman source-graph scanner (legacySourceSpecifiers in packages/agent/src/source-graph.ts) refuses to analyze a project file that creates an aliased dynamic loader — e.g. `const req = require(...)`, `const req = createRequire(...)`, or `const u = URL` — because such an alias makes the file's import surface statically unanalyzable. The build/lock system requires every reachable dependency edge to be provable from source text; an aliased loader could resolve arbitrary modules at runtime, so the file cannot be locked.

Solutions

  1. Remove the aliased loader and import the module statically with an ESM `import` statement so the graph can resolve the edge.
  2. Move the file out of the scanned project graph (make it an installed dependency artifact or exclude it) if it legitimately needs dynamic loading.
  3. For JSON config, use `import config from './x.json' with { type: 'json' }` instead of require-ing it.
  4. Keep the dynamic load behind a worker or subprocess boundary that is not part of the locked source graph.

Example fix

// before
import { createRequire } from "node:module";
const require = createRequire(import.meta.url);
const data = require("./config.json");

// after
import data from "./config.json" with { type: "json" };
Defensive patterns

Strategy: validation

Validate before calling

// Run before caveman build; fails fast on aliased loaders
const loaderAlias = /(const|let|var)\s+\w+\s*=\s*(require|URL)\b|createRequire\s*\(/;
for (const file of projectSourceFiles) {
  if (loaderAlias.test(stripComments(readFileSync(file, "utf8")))) {
    throw new Error(`aliased loader in ${file} blocks locked build`);
  }
}

Prevention

When it happens

Trigger: Running `caveman build`/`dev`/`check` over a source file reachable from the agent definition that contains `createRequire(import.meta.url)`, or assigns `require` or `URL` to a local variable via const/let/var (skipping comments), inside a file the graph scans.

Common situations: Utility modules that use CommonJS interop (`const require = createRequire(import.meta.url)`) in an otherwise-ESM project; code loading JSON or native addons via an aliased require; vendored helper files pulled into the graph; copying CJS idioms into an ESM project.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/5403cf19b5bc1bf5. Report an issue: GitHub.

Appendix: source

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

  }
  cursor = skipSourceTrivia(source, cursor);
  const fromEnd = consumeIdentifierToken(source, cursor, "from");
  if (fromEnd === undefined) return undefined; // Local named type export.
  cursor = skipSourceTrivia(source, fromEnd);
  const literal = readQuotedSpecifier(source, cursor, path);
  if (literal === undefined) throw sourceSyntaxError(path);
  return literal.specifier;
}

function legacySourceSpecifiers(source: string, path: string, code: Uint8Array): string[] {
  const trivia = String.raw`(?:\s|\/\*[\s\S]*?\*\/|\/\/[^\r\n]*(?:\r?\n|$))*`;
  const loaderAliases = [
    new RegExp(String.raw`\bcreateRequire${trivia}\(`),
    new RegExp(String.raw`\b(?:const|let|var)${trivia}[A-Za-z_$][\w$]*${trivia}=${trivia}require\b`),
    new RegExp(String.raw`\b(?:const|let|var)${trivia}[A-Za-z_$][\w$]*${trivia}=${trivia}URL\b`),
  ];
  if (loaderAliases.some((pattern) => hasCodeMatch(source, pattern, code))) {
    throw new Error(`caveman build: aliased source loader is not lockable in ${JSON.stringify(path)}`);
  }
  const specifiers: string[] = [];
  for (const match of source.matchAll(LEGACY_LOAD_START_PATTERN)) {
    const start = match.index;
    const keyword = match[0]!;
    if (!code[start] || !isIdentifierTokenAt(source, start, keyword)) continue;
    if (keyword === "module") {
      assertNoModuleRequireAlias(source, start, path);
      continue;
    }
    if (keyword === "Reflect") {
      assertNoReflectRequireAlias(source, start, path, code);
      continue;
    }
    if (keyword === "globalThis") {
      assertNoGlobalLoaderAlias(source, start, path);
      continue;
    }

View on GitHub (pinned to 3ee70a1026)