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

  1. Prefer the non-throwing `ts.resolveModuleName(name, containingFile, options, host)` and check `resolvedModule` yourself.
  2. Verify the package is installed under `initialDir`'s `node_modules` and has a resolvable entry.
  3. Check the host's `fileExists`/`directoryExists`/`readDirectory` actually see the candidate paths.
  4. 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

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


AI-assisted analysis of microsoft/TypeScript@b465fdbfe1 (2026-08-12). Data as JSON: /api/errors/de95ea2714992a0a. Report an issue: GitHub.