microsoft/TypeScript · error · Error
Could not resolve JS module '${moduleName}' starting at '${i
Error message
Could not resolve JS module '${moduleName}' starting at '${initialDir}'. Looked in: ${failedLookupLocations?.join(", ")} What it means
Thrown by `ts.resolveJSModule` (in `moduleNameResolver.ts`) when its internal worker cannot resolve `moduleName` from `initialDir`. Unlike `resolveModuleName`, this function is a hard resolver (used to expose Node-style resolution from arbitrary locations) and throws rather than returning an unresolved result. The message lists every lookup location that was tried.
Source
Thrown at src/compiler/moduleNameResolver.ts:1681
}
const candidate = normalizePath(combinePaths(baseUrl, moduleName));
if (state.traceEnabled) {
trace(state.host, Diagnostics.Resolving_module_name_0_relative_to_base_url_1_2, moduleName, baseUrl, candidate);
}
return loader(extensions, candidate, !directoryProbablyExists(getDirectoryPath(candidate), state.host), state);
}
/**
* Expose resolution logic to allow us to use Node module resolution logic from arbitrary locations.
* No way to do this with `require()`: https://github.com/nodejs/node/issues/5963
* Throws an error if the module can't be resolved.
*
* @internal
*/
export function resolveJSModule(moduleName: string, initialDir: string, host: ModuleResolutionHost): string {
const { resolvedModule, failedLookupLocations } = tryResolveJSModuleWorker(moduleName, initialDir, host);
if (!resolvedModule) {
throw new Error(`Could not resolve JS module '${moduleName}' starting at '${initialDir}'. Looked in: ${failedLookupLocations?.join(", ")}`);
}
return resolvedModule.resolvedFileName;
}
/** @internal */
export enum NodeResolutionFeatures {
None = 0,
// resolving `#local` names in your own package.json
Imports = 1 << 1,
// resolving `your-own-name` from your own package.json
SelfName = 1 << 2,
// respecting the `.exports` member of packages' package.json files and its (conditional) mappings of export names
Exports = 1 << 3,
// allowing `*` in the LHS of an export to be followed by more content, eg `"./whatever/*.js"`
// not supported in node 12 - https://github.com/nodejs/Release/issues/690
ExportsPatternTrailers = 1 << 4,
// allowing `#/` root imports in package.json imports field
// not supported until mass adoption - https://github.com/nodejs/node/pull/60864
View on GitHub (pinned to b465fdbfe1)
Solutions
- Prefer the non-throwing `ts.resolveModuleName(name, containingFile, options, host)` and check `resolvedModule` yourself.
- Verify the package is installed under `initialDir`'s `node_modules` and has a resolvable entry.
- Check the host's `fileExists`/`directoryExists`/`readDirectory` actually see the candidate paths.
- Pass the real `containingFile`/`initialDir` (the file's directory, not a build root).
Example fix
// before
const path = ts.resolveJSModule("missing-pkg", "./src", host); // throws
// after
const { resolvedModule } = ts.resolveModuleName(
"missing-pkg", "./src/index.ts", compilerOptions, host,
);
if (!resolvedModule) {
// handle missing module without a throw
} Defensive patterns
Strategy: validation
Validate before calling
// Non-throwing resolution check before falling back to resolveJSModule:
const { resolvedModule } = ts.resolveModuleName(
moduleName, containingFile, compilerOptions, host,
);
if (!resolvedModule) throw new Error(moduleName + " not found"); Type guard
function canResolve(name: string, dir: string, host: ts.ModuleResolutionHost, opts: ts.CompilerOptions): boolean {
return !!ts.resolveModuleName(name, dir + "/x.ts", opts, host).resolvedModule;
} Try / catch
try {
const p = ts.resolveJSModule(name, dir, host);
} catch (e) {
// message lists failedLookupLocations; surface to the user as a missing-dep diagnostic
} Prevention
- Use `resolveModuleName` (non-throwing) for user-facing diagnostics; reserve `resolveJSModule` for cases that should hard-fail.
- Run resolution from the file's own directory, not a project root.
- Ensure `node_modules` and package entries are visible to the host.
When it happens
Trigger: Calling `ts.resolveJSModule(name, dir, host)` where `name` is misspelled, not installed, not reachable on the configured `ModuleResolutionHost`, or where the host's directory/file view is wrong. Also reached when the package exists but is excluded by `exports`/`imports` maps or extension filters.
Common situations: Tooling that drives the TS compiler API (language servers, bundlers, custom transforms) calling `resolveJSModule` for a dependency that is not installed; wrong `initialDir` (e.g. a source dir instead of the package root); host that hides `node_modules`; packages without a main entry and no `exports`.
Related errors
- ${option.name} is a string value; tsconfig JSON must be pars
- tracing requires having fs\n(original error: ${e.message ||
- start < 0
- length < 0
- newLength < 0
AI-assisted analysis of microsoft/TypeScript@b465fdbfe1 (2026-08-12).
Data as JSON: /api/errors/de95ea2714992a0a.
Report an issue: GitHub.