Remix "Could not resolve" module in server/browser build in CI
Remix uses esbuild to bundle each route. "Could not resolve X" means the importer references a specifier that esbuild cannot map to a file or package on the runner. The message names the importing file and the missing target.
What this error means
The build fails with ERROR: Could not resolve "..." and a caret pointing at an import in one of your route or app files.
x [ERROR] Could not resolve "~/utils/session"
app/routes/login.tsx:2:28:
2 | import { getSession } from "~/utils/session";
| ^Common causes
A wrong path or unconfigured alias
The ~/ alias or a relative path points at a file that does not exist (a rename, a case-sensitivity mismatch on Linux, or a missing extension).
A dependency not installed on the runner
A bare specifier resolves locally because the package is present, but CI ran without installing it (a devDependency skipped, or a dirty local node_modules).
How to fix it
Verify the path and case exactly
- Confirm the file exists at the exact path and casing the import uses (Linux runners are case-sensitive).
- Ensure the
~/alias maps toapp/in tsconfigpathsand any bundler config. - For a bare package, add it to dependencies and reinstall.
// tsconfig.json
{ "compilerOptions": { "paths": { "~/*": ["./app/*"] } } }Reinstall from the lockfile
Rule out a dirty local install by running a clean, lockfile-driven install on the runner.
npm ciHow to prevent it
- Match import casing to filenames since CI runs on case-sensitive Linux.
- Keep tsconfig
pathsaliases in sync with the bundler. - Declare every imported package in dependencies, not just devDependencies.