Skip to content

Fix crash in non-relative module completions for URLs with a query or fragment - #65341

Open
Ryan Cavanaugh (RyanCavanaugh) wants to merge 1 commit into
microsoft:mainfrom
RyanCavanaugh:fix-completion-nonrelative-url-fragment
Open

Ryan Cavanaugh (RyanCavanaugh) wants to merge 1 commit into
microsoft:mainfrom
RyanCavanaugh:fix-completion-nonrelative-url-fragment

Conversation

@RyanCavanaugh

Copy link
Copy Markdown
Member

Part of #65068 (comment 3: panic handling request textDocument/completion: path must not contain a URL query or fragment from getCompletionEntriesForNonRelativeModules → getCompletionEntriesForDirectoryFragment → RootedDirectoryPath.ResolveDirectory)

Analysis

Module specifiers that look like URLs are only routed to the "relative" completion path when paths is not set:

if isPathRelativeToScript(literalValue) ||
	(options.Paths.Size() == 0 && (tspath.IsRootedDiskPath(literalValue) || tspath.IsUrl(literalValue))) {

So in a project with any paths mapping (e.g. the very common "@/*": ["./src/*"]) and bundler/node16/nodenext resolution, completing inside

import {} from "https://esm.sh/foo?alias=react:preact/|";
import {} from "https://esm.sh/foo#react/|";

goes through getCompletionEntriesForNonRelativeModules. The fragment directory (https://esm.sh/foo?alias=react:preact/) is a URL whose query/fragment is part of the "directory". For each ancestor that has a node_modules folder, ancestorLookup passes the fragment to getCompletionEntriesForDirectoryFragment, which calls nodeModules.ResolveDirectory(fragment). Because the fragment is itself rooted, resolution yields the URL unchanged, and tspath.ToRootedPath panics since a RootedPath must never contain a URL query or fragment.

The same input with a wildcard mapping ("*": ["./src/*"]) hits the same problem one step earlier, in getModulesForPathsPattern: CombinePaths(prefixDirectory, fragmentDirectory) returns the rooted URL and packageDirectory.ResolveDirectory(...) panics.

In 6.0 (src/services/stringCompletions.ts) these paths are just resolved to the URL string; tryDirectoryExists / tryReadDirectory then find nothing, so no file-system completions are produced from them.

Fix

Add a small isURLWithQueryOrFragment helper (a URL path that tspath.TryRootedPathFromAbsolute rejects, i.e. exactly the condition the tspath assertion fires on) and use it to skip directory lookups that can never exist:

  • In getCompletionEntriesForNonRelativeModules, skip listing node_modules/<fragment> when the fragment directory is such a URL. The package.json imports/exports lookups still run as before.
  • In getModulesForPathsPattern, return no matches when the expanded prefix directory is such a URL.

This matches 6.0's observable behavior (no file completions from those directories; ambient-module and other completions are unaffected) and leaves the tspath assertion intact.

Alternatives considered: guarding inside getCompletionEntriesForDirectoryFragment would also fix the reported stack, but that function is shared with relative-module completion, whose crash from #65068 is being fixed separately; keeping the guards in the non-relative callers avoids overlapping changes (if that fix lands, the node_modules guard here becomes redundant but harmless). Weakening the tspath assertion was ruled out.

Tests (new file tsc/internal/fourslash/tests/completionsPaths_nonRelativeUrlWithQuery_test.go):

  • TestCompletionsPaths_nonRelativeUrlWithQuery reproduces the reported stack exactly (panicked before the fix).
  • TestCompletionsPaths_nonRelativeUrlWithQueryWildcardPathMapping covers the getModulesForPathsPattern variant. It uses require(...) in a .ts file (not resolved during program construction) because, with a "*" mapping, an import of such a URL currently also panics in module resolution (tryLoadModuleUsingPaths → resolutionCandidateFromDirectoryPath), which is a separate crash outside the scope of this PR.

Copilot Checklist

I successfully ran the applicable command at the end of my session, and it completed without error:

  • npx hereby validate
  • npx hereby validate --api (for TypeScript API changes)

… fragment

Completing a non-relative module specifier like
"https://esm.sh/pkg?alias=react:preact/" (with "paths" configured) tried to
resolve the URL fragment directory against each ancestor node_modules (and,
for wildcard path mappings, against the paths base directory), which panics in
tspath because a rooted path cannot contain a URL query or fragment. Such a
path can never name a directory on disk, so skip those lookups, matching the
6.0 behavior of producing no file system completions.

Part of microsoft#65068

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: dec07792-6b96-4d6d-b2c8-c8a49abd0ce6
Copilot AI balanced review requested due to automatic review settings November 5, 2026 18:43
@github-project-automation github-project-automation Bot moved this to Not started in PR Backlog Nov 5, 2026
@typescript-automation typescript-automation Bot added Author: Team For Uncommitted Bug PR for untriaged, rejected, closed or missing bug labels Nov 5, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

No active review issues are recorded for the URL module-specifier completion crash fix.

No open issues 🎉

What changed in this PR

Fixes a language-server completion crash for URL module specifiers containing queries or fragments when paths mappings are configured, addressing part of #65068.

Changes:

  • Skips invalid URL directory lookups in non-relative module completions and wildcard path expansion, preserving rooted-path assertions.
  • Adds fourslash regression tests for query and fragment URLs with both alias and wildcard mappings.
File Description
tsc/​internal/​ls/​string_completions.go Adds a URL validation helper and guards both affected directory lookups.
tsc/​internal/​fourslash/​tests/​completionsPaths_nonRelativeUrlWithQuery_test.go Verifies empty completion results without crashes for both mapping scenarios.

Review effort: Balanced

@github-project-automation github-project-automation Bot moved this from Not started to Needs merge in PR Backlog Nov 5, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Author: Team For Uncommitted Bug PR for untriaged, rejected, closed or missing bug

Projects

Status: Needs merge

Development

Successfully merging this pull request may close these issues.

3 participants