
Fixing 'Cannot find module' Errors: TSConfig, Exports, and Extensions
Installed the package, but Node still says 'Cannot find module'? Debugging strategies for package.json exports, file extensions, and monorepo linking issues.

Installed the package, but Node still says 'Cannot find module'? Debugging strategies for package.json exports, file extensions, and monorepo linking issues.
Class is there, but style is missing? Debugging Tailwind CSS like a detective.

Think Android is easier than iOS? Meet Gradle Hell. Learn to fix minSdkVersion conflicts, Multidex limit errors, Namespace issues in Gradle 8.0, and master dependency analysis with `./gradlew dependencies`.

App crashed with TypeError? Learn why 'Null is not a subtype of String' happens and how to make your JSON parsing bulletproof with Zod/Freezed.

I updated the database, but the page still shows old data. We analyze the powerful (and evil) caching mechanism of Next.js 13+ in 4 layers, compare it with React Query, and share practical debugging strategies.

npm install!"I installed a colleague's utility library.
npm install @team/utils
Imported it:
import { formatDate } from '@team/utils';
VSCode is happy. No red lines.
But npm run start crashes immediately.
Error: Cannot find module '@team/utils'
code: 'MODULE_NOT_FOUND'
"Wait, the file exists right inside node_modules! Why can't you find it?"
I nuked node_modules and reinstalled. Same error. It's maddening.
I thought "If the file exists, I can import it."
If node_modules/@team/utils/index.js is there, Node should load it, right?
But Node.js module system (especially ESM) is stricter. It doesn't just check for file existence. It checks "Did this package explicitly allow this file to be exported?"
I also missed that TypeScript's moduleResolution setting completely changes how files are looked up.
I understood it as a "Restaurant Menu."
I walk into the kitchen (node_modules), open the fridge, and see Raw Meat (Internal Module).
I shout, "Give me Raw Meat (import ... from 'package/src/internal')!"
The Waiter (Node.js) refuses: "Sir, that item is not on the Menu (exports)."
Even if the file exists, if it's not on the Menu (exports), it doesn't exist to the outside world.
exports FieldModern libraries use exports heavily.
// node_modules/@team/utils/package.json
{
"name": "@team/utils",
"exports": {
".": "./dist/index.js",
"./date": "./dist/date.js"
// "./string" is NOT here!
}
}
If I try import ... from '@team/utils/string',
I get MODULE_NOT_FOUND. Even if the file exists.
Ask the maintainer to expose it, or stick to the public entry point (.).
moduleResolution)Check tsconfig.json.
{
"compilerOptions": {
"moduleResolution": "Node", // Classic
// "moduleResolution": "Bundler" // Modern (Vite, Next.js)
}
}
If set to Node (Classic), it might misinterpret exports.
For modern frameworks, switch to Bundler.
In Node.js ESM ("type": "module"), File Extensions are Mandatory.
// Worked in CommonJS
import { add } from './math';
// Error in ESM!
// Error: Cannot find module '.../math'
Must be explicit:
import { add } from './math.js';
VSCode might not complain (TS figures it out), but Node.js runtime will crash. (Remember the Map vs Taxi Driver?)
Using pnpm or Yarn Berry creates stricter boundaries.
Package A uses lodash.
Package B imports A.
Package B also imports lodash (but forgot to add it to its own package.json).
In npm/yarn classic, this worked by accident (Hoisting). pnpm blocks this. So you get "Works locally (npm), Fails in CI (pnpm)" errors.
Solution: "Explicitly declare what you use."
Don't rely on transitive dependencies. Add lodash to B/package.json.
Use depcheck to solve "It works on my machine."
npx depcheck
It finds:
We often use aliases like @/components.
// tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
}
}
VSCode and tsc understand this perfectly.
But tsc compiles files while keeping the alias (@/...) intact.
Node.js has NO IDEA what @/ means. So runtime crashes.
"Cannot find module '@/components/Button'..."
../../src/) after compilation.package.json imports field (Modern & Recommended).// package.json
{
"imports": {
"#src/*": "./src/*"
}
}
Recent incident in a Monorepo.
Wanted to use packages/ui inside apps/web.
VSCode auto-imported it as:
import { Button } from '../../packages/ui/src/Button';
Works locally. Explodes in Docker.
Because Docker context only copied apps/web, so the upper directory ../../packages didn't exist in the container.
NEVER cross package boundaries with relative paths.
Use the package name defined in package.json (@myorg/ui).
// ✅ Good
import { Button } from '@myorg/ui';
// ❌ Bad (Local Only)
import { Button } from '../../packages/ui';
We enabled eslint-plugin-import's no-relative-packages rule to ban this forever.
We enabled eslint-plugin-import's no-relative-packages rule to ban this forever.
We love Barrel Files (index.ts) for clean imports:
import { A, B, C } from './utils';
But at scale, this causes Circular Dependencies and Tree Shaking failures.
When Node.js imports a Barrel, it loads everything exported by it.
If you only need A, but the Barrel loads Z, and Z imports A back...
"ReferenceError: Cannot access 'A' before initialization".
Advice: Stop using Barrels for internal modules. Direct imports are faster and safer. Configure VSCode to prefer direct imports.
types Condition in ExportsIn package.json exports, order matters.
For TypeScript 4.7+, the types condition MUST come first.
"exports": {
".": {
"types": "./dist/index.d.ts", // 👈 MUST BE FIRST!
"import": "./dist/index.js",
"require": "./dist/index.cjs"
}
}
If you put import before types, TS might resolve the JS file immediately and ignore the type definition, leading to Could not find a declaration file.
Q: I ran npm install but still can't find module.
A: Restart VSCode (Reload Window). TS Server caches are sticky. If that fails, rm -rf node_modules and npm ci (Clean Install).
Q: Do I always need @types/package-name?
A: Only if the package is written in JS and doesn't ship with d.ts files. If it's pure TS or ships types, you don't need it.
Q: VSCode complains when I add .js extension.
A: Check tsconfig.json. Ensure "moduleResolution": "NodeNext" or "Bundler". Adding extensions is mandatory in ESM, but older TS configs might flag it as an error.
exports field (The Menu), add .js extensions for ESM, and declare all dependencies explicitly for pnpm.