{"record":{"id":"5403cf19b5bc1bf5","repo":"JuliusBrussee/caveman","slug":"caveman-build-aliased-source-loader-is-not-lockable-in-json","errorCode":null,"errorMessage":"caveman build: aliased source loader is not lockable in ${JSON.stringify(path)}","messagePattern":"caveman build: aliased source loader is not lockable in (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/agent/src/source-graph.ts","lineNumber":410,"sourceCode":"  }\n  cursor = skipSourceTrivia(source, cursor);\n  const fromEnd = consumeIdentifierToken(source, cursor, \"from\");\n  if (fromEnd === undefined) return undefined; // Local named type export.\n  cursor = skipSourceTrivia(source, fromEnd);\n  const literal = readQuotedSpecifier(source, cursor, path);\n  if (literal === undefined) throw sourceSyntaxError(path);\n  return literal.specifier;\n}\n\nfunction legacySourceSpecifiers(source: string, path: string, code: Uint8Array): string[] {\n  const trivia = String.raw`(?:\\s|\\/\\*[\\s\\S]*?\\*\\/|\\/\\/[^\\r\\n]*(?:\\r?\\n|$))*`;\n  const loaderAliases = [\n    new RegExp(String.raw`\\bcreateRequire${trivia}\\(`),\n    new RegExp(String.raw`\\b(?:const|let|var)${trivia}[A-Za-z_$][\\w$]*${trivia}=${trivia}require\\b`),\n    new RegExp(String.raw`\\b(?:const|let|var)${trivia}[A-Za-z_$][\\w$]*${trivia}=${trivia}URL\\b`),\n  ];\n  if (loaderAliases.some((pattern) => hasCodeMatch(source, pattern, code))) {\n    throw new Error(`caveman build: aliased source loader is not lockable in ${JSON.stringify(path)}`);\n  }\n  const specifiers: string[] = [];\n  for (const match of source.matchAll(LEGACY_LOAD_START_PATTERN)) {\n    const start = match.index;\n    const keyword = match[0]!;\n    if (!code[start] || !isIdentifierTokenAt(source, start, keyword)) continue;\n    if (keyword === \"module\") {\n      assertNoModuleRequireAlias(source, start, path);\n      continue;\n    }\n    if (keyword === \"Reflect\") {\n      assertNoReflectRequireAlias(source, start, path, code);\n      continue;\n    }\n    if (keyword === \"globalThis\") {\n      assertNoGlobalLoaderAlias(source, start, path);\n      continue;\n    }","sourceCodeStart":392,"sourceCodeEnd":428,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/agent/src/source-graph.ts#L392-L428","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Remove the aliased loader and import the module statically with an ESM `import` statement so the graph can resolve the edge.","Move the file out of the scanned project graph (make it an installed dependency artifact or exclude it) if it legitimately needs dynamic loading.","For JSON config, use `import config from './x.json' with { type: 'json' }` instead of require-ing it.","Keep the dynamic load behind a worker or subprocess boundary that is not part of the locked source graph."],"exampleFix":"// before\nimport { createRequire } from \"node:module\";\nconst require = createRequire(import.meta.url);\nconst data = require(\"./config.json\");\n\n// after\nimport data from \"./config.json\" with { type: \"json\" };","handlingStrategy":"validation","validationCode":"// Run before caveman build; fails fast on aliased loaders\nconst loaderAlias = /(const|let|var)\\s+\\w+\\s*=\\s*(require|URL)\\b|createRequire\\s*\\(/;\nfor (const file of projectSourceFiles) {\n  if (loaderAlias.test(stripComments(readFileSync(file, \"utf8\")))) {\n    throw new Error(`aliased loader in ${file} blocks locked build`);\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use static ESM `import` statements exclusively in project source reachable from agent definitions.","Keep CommonJS interop (createRequire) out of scanned project files; isolate it in external packages or workers.","Load JSON/config with import attributes instead of require.","Run `caveman check` in CI to catch unanalyzable source before attempting a locked build."],"tags":["build","static-analysis","esm","module-resolution"],"backgroundTag":"unsupported-operation","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}