Skip to content

Fix nil dereference crash in loadModuleFromSpecificNodeModulesDirectory#3941

Draft
Copilot wants to merge 3 commits into
mainfrom
copilot/fix-fatal-server-crash
Draft

Fix nil dereference crash in loadModuleFromSpecificNodeModulesDirectory#3941
Copilot wants to merge 3 commits into
mainfrom
copilot/fix-fatal-server-crash

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 18, 2026

Fatal crash when packageInfo.Contents is nil at the typesVersions lookup in loadModuleFromSpecificNodeModulesDirectory. This can occur under concurrent resolution when packageJsonInfoCache.Set() (backed by sync.Map.LoadOrStore) returns a stale entry with nil Contents to a thread that parsed a valid package.json.

  • Add packageInfo.Exists() guard before accessing Contents.GetVersionPaths(), matching the defensive pattern at every other Contents access site in the same function
// Before
if rest != "" {
    versionPaths := packageInfo.Contents.GetVersionPaths(r.getTraceFunc())

// After
if rest != "" && packageInfo.Exists() {
    versionPaths := packageInfo.Contents.GetVersionPaths(r.getTraceFunc())
  • Add compiler test case exercising subpath node_modules resolution (pkg/sub with nested package.json)

Add a `packageInfo.Exists()` guard before accessing `packageInfo.Contents`
at line 1132 in `loadModuleFromSpecificNodeModulesDirectory`. Without this
check, a nil `Contents` field (possible under concurrent resolution races
where `LoadOrStore` returns a stale cache entry) causes a fatal nil-pointer
dereference.

This matches the defensive pattern already used at other `Contents` access
sites in the same function (lines 1078, 1101, 1124).

Fixes #1290

Co-authored-by: DanielRosenwasser <972891+DanielRosenwasser@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix fatal server crash at loadModuleFromSpecificNodeModulesDirectory Fix nil dereference crash in loadModuleFromSpecificNodeModulesDirectory May 18, 2026
Copilot AI requested a review from DanielRosenwasser May 18, 2026 05:42
@DanielRosenwasser
Copy link
Copy Markdown
Member

The test seems insufficient if this is about race conditions. @copilot is there a better test here? Possibly a server test or a programmatic test of our APIs?

Remove the compiler test (nodeModulesSubpathResolution.ts) which didn't
exercise the actual race condition. Add a Go unit test
(TestResolveSubpathNilContentsRace) that deterministically reproduces the
nil-Contents cache race using a flipFileExistsFS wrapper:

1. Two goroutines resolve `pkg/sub` concurrently, both miss the cache
2. First caller's FileExists returns false → stores nil-Contents entry
3. Second caller's FileExists returns true → ReadFile is gated until the
   first's Set completes → its Set (LoadOrStore) returns the stale
   nil-Contents entry
4. Without the Exists() guard: nil dereference panic
5. With the guard: gracefully skipped

The test deterministically fails when the fix is reverted.

Co-authored-by: DanielRosenwasser <972891+DanielRosenwasser@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 18, 2026

The test seems insufficient if this is about race conditions. @copilot is there a better test here? Possibly a server test or a programmatic test of our APIs?

Replaced the compiler test with a programmatic race condition test in resolver_test.go (TestResolveSubpathNilContentsRace). It uses a flipFileExistsFS wrapper to deterministically reproduce the LoadOrStore cache race:

  1. Two goroutines resolve pkg/sub concurrently — both miss the package.json info-cache
  2. The first caller's FileExists returns false → stores a nil-Contents cache entry
  3. The second caller's FileExists returns true, but ReadFile is gated until the first's Set completes → its Set (LoadOrStore) returns the stale nil-Contents entry
  4. Without the Exists() guard → nil dereference panic; with the guard → safely skipped

I verified it deterministically fails when the fix is reverted. See commit 3dc7a86.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fatal server crash at loadModuleFromSpecificNodeModulesDirectory

2 participants