Link to the code that reproduces this issue
https://github.com/gardsa/my-minimal-nextjs-issue-reproduction
To Reproduce
- Clone the reproduction repository (linked above)
- From the repo root, install dependencies:
- Run the build from the app directory:
cd apps/my-app && npx next build
- 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.
Link to the code that reproduces this issue
https://github.com/gardsa/my-minimal-nextjs-issue-reproduction
To Reproduce
Current vs. Expected behavior
Expected:
next buildcompletes successfully. Path aliases defined in the sharedtsconfig.base.jsonresolve correctly for both webpack and the TypeScript type-checker.Actual: Webpack/Turbopack compilation succeeds, but the TypeScript check step that runs immediately after fails:
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
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.jsadds a TypeScript 6 compatibility block that rewritespathsto be relative to the app dir and deletesbaseUrl:For an app at
apps/my-app/,normalizedBaseUrlis'../..', so"libs/shared-lib/src/*"becomes"../../libs/shared-lib/src/*"— correct relative to the app dir.After
baseUrlis deleted, TypeScript's module resolver callsgetPathsBasePath:TypeScript sets
pathsBasePathto the directory of the tsconfig that defines thepathskey. In a monorepo this is the repo root (wheretsconfig.base.jsonlives), not the app directory.TypeScript therefore resolves
"../../libs/shared-lib/src/*"relative to the repo root:Why webpack/Turbopack works but TypeScript does not:
load-jsconfig.jsusespath.dirname(tsConfigPath)(the app dir) as the implicitbaseUrlfallback whenbaseUrlis absent.JsConfigPathsPluginresolves the rewritten../../...paths relative to the app dir, which is correct. The TypeScript checker goes through the samegetTypeScriptConfigurationcall but usespathsBasePathinstead, which points at the wrong base directory.Proposed fix
After deleting
baseUrl, setpathsBasePathtopath.dirname(tsConfigPath)so TypeScript uses the same resolution base as webpack: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 chainpathswere 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 rejectsbaseUrlas deprecated before Next.js can influence resolution. ThegetTypeScriptConfiguration.jscompat code in 16.2.x is the correct approach; it is just missing the one-linepathsBasePathupdate.