nextjs - 💡(How to fix) Fix `getTypeScriptConfiguration`: TypeScript 6 path rewriting deletes `baseUrl` but does not update `pathsBasePath`, breaking module resolution in monorepos with shared base tsconfig [1 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#93336Fetched 2026-04-29 06:10:40
View on GitHub
Comments
0
Participants
1
Timeline
3
Reactions
0
Author
Participants
Timeline (top)
labeled ×2issue_type_added ×1

Error Message

Type error: Cannot find module '@scope/shared-lib/index' or its corresponding type declarations.

Root Cause

getTypeScriptConfiguration.js adds a TypeScript 6 compatibility block that rewrites paths to be relative to the app dir and deletes baseUrl:

if (semver.gte(typescript.version, '6.0.0')) {
    const parsedBaseUrl = result.options.baseUrl;
    if (typeof parsedBaseUrl === 'string' && parsedBaseUrl.length > 0) {
        const normalizedBaseUrl = getNormalizedBaseUrlForPaths(parsedBaseUrl, tsConfigPath);
        result.options.paths = rewritePathAliasesWithoutBaseUrl(normalizedBaseUrl, result.options.paths);
        delete result.options.baseUrl;
        // ← pathsBasePath is NOT updated here
    }
}

For an app at apps/my-app/, normalizedBaseUrl is '../..', so "libs/shared-lib/src/*" becomes "../../libs/shared-lib/src/*" — correct relative to the app dir.

After baseUrl is deleted, TypeScript's module resolver calls getPathsBasePath:

// typescript/lib/typescript.js
function getPathsBasePath(options, host) {
    if (!options.paths) return undefined;
    return options.baseUrl ??
        options.pathsBasePath ||
        host.getCurrentDirectory();
}

TypeScript sets pathsBasePath to the directory of the tsconfig that defines the paths key. In a monorepo this is the repo root (where tsconfig.base.json lives), not the app directory.

TypeScript therefore resolves "../../libs/shared-lib/src/*" relative to the repo root:

path.join('/repo-root', '../../libs/shared-lib/src/foo')
// → '/libs/shared-lib/src/foo'   ← does not exist

Why webpack/Turbopack works but TypeScript does not: load-jsconfig.js uses path.dirname(tsConfigPath) (the app dir) as the implicit baseUrl fallback when baseUrl is absent. JsConfigPathsPlugin resolves the rewritten ../../... paths relative to the app dir, which is correct. The TypeScript checker goes through the same getTypeScriptConfiguration call but uses pathsBasePath instead, which points at the wrong base directory.

Fix Action

Workaround

None that avoids modifying node_modules. Pinning to Next.js 16.1.0 is not viable because that version predates the TypeScript 6 compatibility code entirely — TypeScript 6 rejects baseUrl as deprecated before Next.js can influence resolution. The getTypeScriptConfiguration.js compat code in 16.2.x is the correct approach; it is just missing the one-line pathsBasePath update.

Code Example

npm install

---

cd apps/my-app && npx next build

---

Type error: Cannot find module '@scope/shared-lib/index' or its corresponding type declarations.

---

Running TypeScript ...
Failed to type check.

./src/app/page.tsx:1:26
Type error: Cannot find module '@scope/shared-lib/index' or its corresponding type declarations.

---

Operating System:
  Platform: linux
  Arch: arm64
Binaries:
  Node: 24.14.1
  npm: 11.11.0
  Yarn: 1.22.22
Relevant Packages:
  next: 16.2.4
  react: 19.2.5
  react-dom: 19.2.5
  typescript: 6.0.3

---

if (semver.gte(typescript.version, '6.0.0')) {
    const parsedBaseUrl = result.options.baseUrl;
    if (typeof parsedBaseUrl === 'string' && parsedBaseUrl.length > 0) {
        const normalizedBaseUrl = getNormalizedBaseUrlForPaths(parsedBaseUrl, tsConfigPath);
        result.options.paths = rewritePathAliasesWithoutBaseUrl(normalizedBaseUrl, result.options.paths);
        delete result.options.baseUrl;
        // ← pathsBasePath is NOT updated here
    }
}

---

// typescript/lib/typescript.js
function getPathsBasePath(options, host) {
    if (!options.paths) return undefined;
    return options.baseUrl ??
        options.pathsBasePath ||
        host.getCurrentDirectory();
}

---

path.join('/repo-root', '../../libs/shared-lib/src/foo')
// → '/libs/shared-lib/src/foo'   ← does not exist

---

result.options.paths = rewritePathAliasesWithoutBaseUrl(normalizedBaseUrl, result.options.paths);
delete result.options.baseUrl;
result.options.pathsBasePath = path.dirname(tsConfigPath); // ← add this
RAW_BUFFERClick to expand / collapse

Link to the code that reproduces this issue

https://github.com/gardsa/my-minimal-nextjs-issue-reproduction

To Reproduce

  1. Clone the reproduction repository (linked above)
  2. From the repo root, install dependencies:
    npm install
  3. Run the build from the app directory:
    cd apps/my-app && npx next build
  4. Observe that Turbopack/webpack compilation succeeds but the TypeScript type-check step immediately after fails:
    Type error: Cannot find module '@scope/shared-lib/index' or its corresponding type declarations.

Current vs. Expected behavior

Expected: next build completes successfully. Path aliases defined in the shared tsconfig.base.json resolve correctly for both webpack and the TypeScript type-checker.

Actual: Webpack/Turbopack compilation succeeds, but the TypeScript check step that runs immediately after fails:

Running TypeScript ...
Failed to type check.

./src/app/page.tsx:1:26
Type error: Cannot find module '@scope/shared-lib/index' or its corresponding type declarations.

This affects every path alias defined in the shared base tsconfig. Aliases defined directly in the app-level tsconfig are not affected.

Provide environment information

Operating System:
  Platform: linux
  Arch: arm64
Binaries:
  Node: 24.14.1
  npm: 11.11.0
  Yarn: 1.22.22
Relevant Packages:
  next: 16.2.4
  react: 19.2.5
  react-dom: 19.2.5
  typescript: 6.0.3

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

TypeScript, Module Resolution

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

next build (local)

Additional context

Root cause

getTypeScriptConfiguration.js adds a TypeScript 6 compatibility block that rewrites paths to be relative to the app dir and deletes baseUrl:

if (semver.gte(typescript.version, '6.0.0')) {
    const parsedBaseUrl = result.options.baseUrl;
    if (typeof parsedBaseUrl === 'string' && parsedBaseUrl.length > 0) {
        const normalizedBaseUrl = getNormalizedBaseUrlForPaths(parsedBaseUrl, tsConfigPath);
        result.options.paths = rewritePathAliasesWithoutBaseUrl(normalizedBaseUrl, result.options.paths);
        delete result.options.baseUrl;
        // ← pathsBasePath is NOT updated here
    }
}

For an app at apps/my-app/, normalizedBaseUrl is '../..', so "libs/shared-lib/src/*" becomes "../../libs/shared-lib/src/*" — correct relative to the app dir.

After baseUrl is deleted, TypeScript's module resolver calls getPathsBasePath:

// typescript/lib/typescript.js
function getPathsBasePath(options, host) {
    if (!options.paths) return undefined;
    return options.baseUrl ??
        options.pathsBasePath ||
        host.getCurrentDirectory();
}

TypeScript sets pathsBasePath to the directory of the tsconfig that defines the paths key. In a monorepo this is the repo root (where tsconfig.base.json lives), not the app directory.

TypeScript therefore resolves "../../libs/shared-lib/src/*" relative to the repo root:

path.join('/repo-root', '../../libs/shared-lib/src/foo')
// → '/libs/shared-lib/src/foo'   ← does not exist

Why webpack/Turbopack works but TypeScript does not: load-jsconfig.js uses path.dirname(tsConfigPath) (the app dir) as the implicit baseUrl fallback when baseUrl is absent. JsConfigPathsPlugin resolves the rewritten ../../... paths relative to the app dir, which is correct. The TypeScript checker goes through the same getTypeScriptConfiguration call but uses pathsBasePath instead, which points at the wrong base directory.

Proposed fix

After deleting baseUrl, set pathsBasePath to path.dirname(tsConfigPath) so TypeScript uses the same resolution base as webpack:

result.options.paths = rewritePathAliasesWithoutBaseUrl(normalizedBaseUrl, result.options.paths);
delete result.options.baseUrl;
result.options.pathsBasePath = path.dirname(tsConfigPath); // ← add this

This ensures the rewritten ../../libs/... paths are resolved from the app's tsconfig directory, producing the correct absolute path regardless of where in the extends chain paths were originally defined.

Workaround

None that avoids modifying node_modules. Pinning to Next.js 16.1.0 is not viable because that version predates the TypeScript 6 compatibility code entirely — TypeScript 6 rejects baseUrl as deprecated before Next.js can influence resolution. The getTypeScriptConfiguration.js compat code in 16.2.x is the correct approach; it is just missing the one-line pathsBasePath update.

extent analysis

TL;DR

  • The most likely fix is to update the getTypeScriptConfiguration.js file to set pathsBasePath to the app directory after deleting baseUrl, ensuring consistent path resolution for both webpack and TypeScript.

Guidance

  • Identify the getTypeScriptConfiguration.js file and locate the TypeScript 6 compatibility block that rewrites paths and deletes baseUrl.
  • Update this block to include the line result.options.pathsBasePath = path.dirname(tsConfigPath); after deleting baseUrl, as proposed in the issue.
  • Verify that the next build command completes successfully after applying this change.
  • Test the application to ensure that the fix does not introduce any new issues.

Example

if (semver.gte(typescript.version, '6.0.0')) {
    const parsedBaseUrl = result.options.baseUrl;
    if (typeof parsedBaseUrl === 'string' && parsedBaseUrl.length > 0) {
        const normalizedBaseUrl = getNormalizedBaseUrlForPaths(parsedBaseUrl, tsConfigPath);
        result.options.paths = rewritePathAliasesWithoutBaseUrl(normalizedBaseUrl, result.options.paths);
        delete result.options.baseUrl;
        result.options.pathsBasePath = path.dirname(tsConfigPath); // Add this line
    }
}

Notes

  • This fix assumes that the issue is caused by the inconsistency in path resolution between webpack and TypeScript, as described in the issue.
  • The proposed fix updates the getTypeScriptConfiguration.js file to set pathsBasePath to the app directory, ensuring consistent path resolution.

Recommendation

  • Apply the proposed fix by updating the getTypeScriptConfiguration.js file, as it directly addresses the identified issue and ensures consistent path resolution for both webpack and TypeScript.

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…

Still need to ship something?

×6

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

Back to top recommendations

TRENDING