Skip to content

Missing exports target: fileList gets a bare package.json plus only a soft warning — traced output fails at runtime with a misleading MODULE_NOT_FOUND #605

Description

@linyiru

Summary

When a dependency's exports-resolved entry file is missing on disk (e.g. a package manager left a package directory in a torn state: package.json from one version, dist/ from another), nodeFileTrace:

  1. records a Failed to resolve dependency warning in result.warnings (good), but
  2. still emits the package's package.json alone into fileList, with no entry file and no marker of the failure.

Deploying that file set produces a delayed, misleading runtime failure: Node finds the package.json, resolves its exports map, and crashes with Cannot find module '<pkg>/dist/…' — a deep internal path that implicates the package rather than the trace/install. If nft emitted nothing for the package, the runtime error would at least be the legible Cannot find module '<pkg>'.

Reproduction (self-contained)

mkdir nft-torn-repro && cd nft-torn-repro
npm init -y >/dev/null
npm i lru-cache@11.5.1 @vercel/nft@1.10.2 >/dev/null
echo "const { LRUCache } = require('lru-cache'); console.log('ok');" > entry.js

# Simulate the torn install state: the exports target for require+node
# conditions ("./dist/commonjs/node/index.min.js") is missing on disk.
rm node_modules/lru-cache/dist/commonjs/node/index.min.js

node -e "
const { nodeFileTrace } = require('@vercel/nft');
nodeFileTrace(['entry.js'], { base: process.cwd() }).then(r => {
  console.log('fileList:', [...r.fileList].filter(f => f.includes('lru-cache')));
  console.log('warnings:', [...r.warnings].map(w => w.message.split('\n')[0]));
});
"

Output (nft 1.10.2, Node 22):

fileList: [ 'node_modules/lru-cache/package.json' ]
warnings: [ 'Failed to resolve dependency "lru-cache":' ]

Copy fileList to a deploy target and run entry.js there:

Error: Cannot find module '/var/task/node_modules/lru-cache/dist/commonjs/node/index.min.js'

Note the warning's full message already contains that exact path — the diagnostic exists at trace time, but nothing in the emitted file set reflects it.

Real-world impact

This is not hypothetical: we traced a production 500 on a Vercel serverless function to exactly this artifact shape — the deployed function filesystem contained lru-cache's 11.x package.json but not the file its exports map resolves to, and every SSR request to the affected route failed with the deep MODULE_NOT_FOUND above, via a serverExternalPackages runtime require() chain (jsdom → cssstyle → @asamuzakjp/css-color → lru-cache). The build had succeeded with a green log (the consumer pipeline does not surface result.warnings). Diagnosing it took build-log forensics plus local reconstruction of the file-set shape; a visible trace-time diagnostic would have answered it immediately.

There is also a fully self-contained Next.js-level reproduction (green next build, zero warnings, deployed standalone output 500s / silently drops page metadata): https://github.com/linyiru/next-silent-tracing-failure

Suggestions (any subset would help)

  1. When exports resolution fails because the target file does not exist, don't emit the bare package.json for that package (or tag it in the result), so the shipped artifact fails legibly instead of pointing at the package's internals.
  2. Offer an opt-in strict mode (e.g. throwOnResolveError) so CI/builders can hard-fail on this class of trace failure.
  3. At minimum, document that fileList may contain package.json-only entries for packages whose resolution failed, and that consumers should surface result.warnings — the major consumers (Next.js output file tracing, the Vercel builder) currently swallow them.

Environment: @vercel/nft 1.10.2, Node.js 22.22, macOS/Linux (reproduced on both).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions