nextjs - 💡(How to fix) Fix Turbopack file tracing includes repository root: can’t access reasons during real build [15 comments, 3 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
vercel/next.js#84960Fetched 2026-04-08 02:17:52
View on GitHub
Comments
15
Participants
3
Timeline
23
Reactions
2
Author
Timeline (top)
commented ×15subscribed ×5issue_type_added ×1labeled ×1

Error Message

console.error('Error while tracing files:', err); console.error('fs not available in this context. First 20 traced files:'); console.error(out.slice(0, 20).map(o => ${o.file} -> ${o.types}).join('\n')); try { console.error('Failed to write nft debug:', e && e.stack ? e.stack : String(e)); } catch (_) {}

Fix Action

Fix / Workaround

  1. Patching Next’s bundled @vercel/nft I temporarily patched node_modules/next/dist/compiled/@vercel/nft/index.js to write out {file, types, parents} for each entry in r.fileList: Result: in a real build, the debug output only shows entries under node_modules. I don’t see the application files or repo-root files that appear in *.js.nft.json, so I still can’t identify the chain of inclusion.
<summary>The patch to `nodeFileTrace`</summary>

I’d be happy to contribute documentation or even a small patch to improve the debug tooling, if that’s within reach for contributors.

Code Example

{
  "version": 1,
  "files": [
    "../../../../../../Makefile",
    "../../../../../../README.md",
    "../../../../../../components.json",
    "...",
    "../../../../chunks/ssr/src_messages_fr_json_3c6a9a9a._.js",
    "./page/react-loadable-manifest.json",
    "./page_client-reference-manifest.js"
  ]
}

---

Operating System:
  Platform: win32
  Arch: x64
  Version: Windows 11 Pro
  Available memory (MB): 16093
  Available CPU cores: 16
Binaries:
  Node: 22.20.0
  npm: 11.6.2
  Yarn: N/A
  pnpm: N/A
Relevant Packages:
  next: 15.5.5 // Latest available version is detected (15.5.5).
  eslint-config-next: 15.5.5
  react: 19.2.0
  react-dom: 19.2.0
  typescript: 5.9.3
Next.js Config:
  output: N/A

---

#!/usr/bin/env node
import { nodeFileTrace } from '@vercel/nft';
import path from 'path';
import { fileURLToPath } from 'url';

const __dirname = path.dirname(fileURLToPath(import.meta.url));

function printStack(file, reasons, stdout, cwd, depth = 0) {
  const indent = '  '.repeat(depth);
  stdout.push(`${indent}${file}`);

  const reason = reasons.get(file);

  if (
    !reason ||
    !reason.parents ||
    (reason.type.length === 1 &&
      reason.type.includes('initial') &&
      reason.parents.size === 0)
  ) {
    return;
  }

  // Show why this file was included
  const types = Array.from(reason.type).join(', ');
  stdout.push(`${indent}  └─ Reason: ${types}`);

  for (let parent of reason.parents) {
    printStack(parent, reasons, stdout, cwd, depth + 1);
  }
}

async function main() {
  const files = [
    path.join(__dirname, '.next/server/app/[locale]/PATH_TO_MY_PAGE/page.js'),
  ];

  console.log(`Tracing files:\n${files.join('\n')}\n`);

  try {
    const { fileList, reasons } = await nodeFileTrace(files, {
      base: process.cwd(),
      processCwd: process.cwd(),
      mixedModules: true,
      conditions: ['node', 'import', 'require', 'default', 'module', 'production'],
      ignore: [
        '**/*.map',
        '**/node_modules/.bin/**',
        '**/.next/cache/**',
        '**/next/dist/**',
      ],
      exportsOnly: true,
    });

    console.log('Traced files with dependency chains:');
    console.log('=====================================\n');

    for (const file of fileList) {
      console.log(`📁 ${file}`);
      const stack = [];
      printStack(file, reasons, stack, process.cwd());

      // Remove the first line since we already printed the file name
      stack.shift();

      if (stack.length > 0) {
        console.log('Dependencies:');
        console.log(stack.join('\n'));
      } else {
        console.log('  └─ Entry point (no dependencies)');
      }
      console.log('');
    }

    console.log(`Total: ${fileList.size} files`);
  } catch (err) {
    console.error('Error while tracing files:', err);
    process.exit(1);
  }
}

main();

---

const s = {
  fileList: r.fileList,
  esmFileList: r.esmFileList,
  reasons: r.reasons,
  warnings: r.warnings,
};

const path = (() => {
  try { return require('path'); } catch (_) { return eval('require')('path'); }
})();

let fs;
try {
  // Prefer normal require when available
  if (typeof require === 'function') {
    fs = require('fs');
  } else {
    // Fallback to eval('require') which avoids static analysis by bundlers
    fs = eval('require')('fs');
  }
} catch (err) {
  // Last-ditch async fallback if running in ESM async context
  try {
    // This requires the surrounding function to be async. Use only if safe.
    // const mod = await import('fs'); fs = mod.default || mod;
    fs = null;
  } catch (e) {
    fs = null;
  }
}

try {
  const out = []; // build your 'out' array earlier as before
  for (const file of r.fileList) {
    const reason = r.reasons.get(file);
    const types = reason ? Array.from(reason.type).join(', ') : '(no reason)';
    const parents = reason && reason.parents ? Array.from(reason.parents) : [];
    out.push({ file, types, parents });
  }

  if (fs) {
    const outFile = path.join(process.cwd(), '.next', 'nft-debug.json');
    // ensure dir exists
    try { fs.mkdirSync(path.dirname(outFile), { recursive: true }); } catch (_) {}
    fs.writeFileSync(outFile, JSON.stringify(out, null, 2));
    console.log('NFT debug written to:', outFile);
  } else {
    // If fs is not available, emit a small signal to the console with summary info
    // Avoid printing the entire trace to stdout to prevent blocking
    console.error('fs not available in this context. First 20 traced files:');
    console.error(out.slice(0, 20).map(o => `${o.file} -> ${o.types}`).join('\n'));
  }
} catch (e) {
  try { console.error('Failed to write nft debug:', e && e.stack ? e.stack : String(e)); } catch (_) {}
}
  return s;
}
RAW_BUFFERClick to expand / collapse

Link to the code that reproduces this issue

https://github.com/gadcam/next.js

To Reproduce

Run: npx next build --turbopack

Inspect generated trace files: **/*.js.nft.json

Observe that, for certain page.ts files, file tracing appears to include the entire repository root.

Current vs. Expected behavior

Actual behavior

File tracing includes the repo root (e.g., README.md, Makefile, etc.) for some pages, leading to oversized Serverless Functions.

During a real Next/Turbopack build, I cannot surface the reasons map from nodeFileTrace, so I can’t understand why these files are pulled in.

Expected behavior

File tracing should not include unrelated files from the repository root for these entries.

There should be a supported way to inspect the reasons map (or equivalent) in the real build pipeline to explain why each file is traced.

Example of output

{
  "version": 1,
  "files": [
    "../../../../../../Makefile",
    "../../../../../../README.md",
    "../../../../../../components.json",
    "...",
    "../../../../chunks/ssr/src_messages_fr_json_3c6a9a9a._.js",
    "./page/react-loadable-manifest.json",
    "./page_client-reference-manifest.js"
  ]
}

Provide environment information

Operating System:
  Platform: win32
  Arch: x64
  Version: Windows 11 Pro
  Available memory (MB): 16093
  Available CPU cores: 16
Binaries:
  Node: 22.20.0
  npm: 11.6.2
  Yarn: N/A
  pnpm: N/A
Relevant Packages:
  next: 15.5.5 // Latest available version is detected (15.5.5).
  eslint-config-next: 15.5.5
  react: 19.2.0
  react-dom: 19.2.0
  typescript: 5.9.3
Next.js Config:
  output: N/A

Which area(s) are affected? (Select all that apply)

Turbopack

Which stage(s) are affected? (Select all that apply)

next build (local), Vercel (Deployed)

Additional context

What I tried

  1. Running @vercel/nft directly Result: this traces far fewer files than the real Next/Turbopack build, so it doesn’t mirror real build conditions and doesn’t explain the oversized output.
<details> <summary>The script</summary>
#!/usr/bin/env node
import { nodeFileTrace } from '@vercel/nft';
import path from 'path';
import { fileURLToPath } from 'url';

const __dirname = path.dirname(fileURLToPath(import.meta.url));

function printStack(file, reasons, stdout, cwd, depth = 0) {
  const indent = '  '.repeat(depth);
  stdout.push(`${indent}${file}`);

  const reason = reasons.get(file);

  if (
    !reason ||
    !reason.parents ||
    (reason.type.length === 1 &&
      reason.type.includes('initial') &&
      reason.parents.size === 0)
  ) {
    return;
  }

  // Show why this file was included
  const types = Array.from(reason.type).join(', ');
  stdout.push(`${indent}  └─ Reason: ${types}`);

  for (let parent of reason.parents) {
    printStack(parent, reasons, stdout, cwd, depth + 1);
  }
}

async function main() {
  const files = [
    path.join(__dirname, '.next/server/app/[locale]/PATH_TO_MY_PAGE/page.js'),
  ];

  console.log(`Tracing files:\n${files.join('\n')}\n`);

  try {
    const { fileList, reasons } = await nodeFileTrace(files, {
      base: process.cwd(),
      processCwd: process.cwd(),
      mixedModules: true,
      conditions: ['node', 'import', 'require', 'default', 'module', 'production'],
      ignore: [
        '**/*.map',
        '**/node_modules/.bin/**',
        '**/.next/cache/**',
        '**/next/dist/**',
      ],
      exportsOnly: true,
    });

    console.log('Traced files with dependency chains:');
    console.log('=====================================\n');

    for (const file of fileList) {
      console.log(`📁 ${file}`);
      const stack = [];
      printStack(file, reasons, stack, process.cwd());

      // Remove the first line since we already printed the file name
      stack.shift();

      if (stack.length > 0) {
        console.log('Dependencies:');
        console.log(stack.join('\n'));
      } else {
        console.log('  └─ Entry point (no dependencies)');
      }
      console.log('');
    }

    console.log(`Total: ${fileList.size} files`);
  } catch (err) {
    console.error('Error while tracing files:', err);
    process.exit(1);
  }
}

main();
</details>
  1. Patching Next’s bundled @vercel/nft I temporarily patched node_modules/next/dist/compiled/@vercel/nft/index.js to write out {file, types, parents} for each entry in r.fileList: Result: in a real build, the debug output only shows entries under node_modules. I don’t see the application files or repo-root files that appear in *.js.nft.json, so I still can’t identify the chain of inclusion.
<details> <summary>The patch to `nodeFileTrace`</summary>
const s = {
  fileList: r.fileList,
  esmFileList: r.esmFileList,
  reasons: r.reasons,
  warnings: r.warnings,
};

const path = (() => {
  try { return require('path'); } catch (_) { return eval('require')('path'); }
})();

let fs;
try {
  // Prefer normal require when available
  if (typeof require === 'function') {
    fs = require('fs');
  } else {
    // Fallback to eval('require') which avoids static analysis by bundlers
    fs = eval('require')('fs');
  }
} catch (err) {
  // Last-ditch async fallback if running in ESM async context
  try {
    // This requires the surrounding function to be async. Use only if safe.
    // const mod = await import('fs'); fs = mod.default || mod;
    fs = null;
  } catch (e) {
    fs = null;
  }
}

try {
  const out = []; // build your 'out' array earlier as before
  for (const file of r.fileList) {
    const reason = r.reasons.get(file);
    const types = reason ? Array.from(reason.type).join(', ') : '(no reason)';
    const parents = reason && reason.parents ? Array.from(reason.parents) : [];
    out.push({ file, types, parents });
  }

  if (fs) {
    const outFile = path.join(process.cwd(), '.next', 'nft-debug.json');
    // ensure dir exists
    try { fs.mkdirSync(path.dirname(outFile), { recursive: true }); } catch (_) {}
    fs.writeFileSync(outFile, JSON.stringify(out, null, 2));
    console.log('NFT debug written to:', outFile);
  } else {
    // If fs is not available, emit a small signal to the console with summary info
    // Avoid printing the entire trace to stdout to prevent blocking
    console.error('fs not available in this context. First 20 traced files:');
    console.error(out.slice(0, 20).map(o => `${o.file} -> ${o.types}`).join('\n'));
  }
} catch (e) {
  try { console.error('Failed to write nft debug:', e && e.stack ? e.stack : String(e)); } catch (_) {}
}
  return s;
}
</details>

Questions / help requested

How can I surface the reasons map during a real Next/Turbopack build? Is there a supported hook/flag to dump trace reasons (per entry) that mirrors the production build?

If this isn’t possible today, what would you recommend adding (CLI flag, env var, or debug callback) so we can reliably get:

  • the traced file list and
  • the reasons/parent chain for each file

I’d be happy to contribute documentation or even a small patch to improve the debug tooling, if that’s within reach for contributors.

extent analysis

TL;DR

To address the issue of oversized Serverless Functions due to unnecessary file tracing, modify the nodeFileTrace function to include a debug flag or callback that surfaces the reasons map during a real Next/Turbopack build.

Guidance

  1. Modify nodeFileTrace: Introduce a debug flag or callback in nodeFileTrace to output the reasons map for each file, allowing for better understanding of why certain files are being traced.
  2. Implement a custom tracing hook: Consider adding a custom hook in Next.js that intercepts the tracing process and logs the reasons map for each file, providing insights into the tracing decisions.
  3. Use environment variables for debug output: Utilize environment variables to control the debug output of nodeFileTrace, enabling the reasons map to be surfaced only when necessary.
  4. Contribute to Next.js debug tooling: Collaborate with the Next.js community to enhance the debug tooling, potentially adding a CLI flag or debug callback to facilitate tracing analysis.

Example

// Example of a modified nodeFileTrace function with a debug flag
async function nodeFileTrace(files, options) {
  // ...
  if (options.debug) {
    const reasonsMap = {};
    for (const file of fileList) {
      const reason = reasons.get(file);
      reasonsMap[file] = {
        types: Array.from(reason.type),
        parents: Array.from(reason.parents),
      };
    }
    console.log('Reasons map:', reasonsMap);
  }
  // ...
}

Notes

The proposed solution focuses on enhancing the debug capabilities of nodeFileTrace to provide better insights into the tracing process. However, the exact implementation details may vary depending on the specific requirements and constraints of the Next.js project.

Recommendation

Apply a workaround by modifying the nodeFileTrace function to include a debug flag or callback, allowing for the reasons map to be surfaced during a real Next/Turbopack build. This approach enables a better understanding of the tracing decisions and can help identify the root cause of the oversized Serverless Functions issue.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

File tracing should not include unrelated files from the repository root for these entries.

There should be a supported way to inspect the reasons map (or equivalent) in the real build pipeline to explain why each file is traced.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

nextjs - 💡(How to fix) Fix Turbopack file tracing includes repository root: can’t access reasons during real build [15 comments, 3 participants]