Repository navigation
API: closeProjects then openFiles crashes tsgo (nil ChangeIf in acquireConfigForFile) #65071
Description
🔎 Search Terms
closeProjects, openFiles, acquireConfigForFile, configFileRegistryBuilder, SyncMapEntry ChangeIf, nil pointer dereference, typescript/sync, tombstone LoadOrStore
🕗 Version & Regression Information
- Present in
typescript@7.1.0-dev.20261030.1(next) - The
closeProjects/openFilessnapshot API is new in 7.1, so this could not be tested on 7.0.2 - Related but distinct from:
- API:
snapshot.updatewith bothremoveProgramsandcreateProgramscrashes tsgo (nil dereference inProjectCollectionBuilder.HandleAPIRequest) #64911 (removePrograms+createProgramson synthetic programs) - API server panics in acquireConfigForFile when opening a JS file a tsconfig does not include #65054 (
openFilesof a.mjsnext to an emptytsconfig.jsonthat does not include it)
- API:
⏯ Playground Link
N/A — typescript/sync API, not the language/checker.
💻 Code
// repro.mjs — run with: node repro.mjs (typescript@next installed)
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { API } from "typescript/sync";
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "ts-close-openfiles-"));
const config = path.join(dir, "tsconfig.json");
const file = path.join(dir, "a.ts");
fs.writeFileSync(config, JSON.stringify({ compilerOptions: { strict: true }, files: ["a.ts"] }));
fs.writeFileSync(file, "export const a = 1;\n");
const api = new API();
const snap = api.createSnapshot({
openProjects: [config],
openFiles: [file],
});
// Any of these panics the tsgo child:
snap.update({ closeProjects: [config], openFiles: [file] });
// Also panics as separate updates:
// const mid = snap.update({ closeProjects: [config] });
// mid.update({ openFiles: [file] });
// mid.update({ openProjects: [config], openFiles: [file] });🙁 Actual behavior
The API server panics with a nil dereference; the client then sees Unexpected EOF while reading from child process. Later requests on the same API fail with EPIPE.
panic: runtime error: invalid memory address or nil pointer dereference
[signal ...]
github.com/microsoft/TypeScript/tsc/internal/project/dirty.(*SyncMapEntry[...]).ChangeIf(...)
tsc/internal/project/dirty/syncmap.go:128
github.com/microsoft/TypeScript/tsc/internal/project.(*configFileRegistryBuilder).acquireConfigForFile(...)
tsc/internal/project/configfileregistrybuilder.go:322
github.com/microsoft/TypeScript/tsc/internal/project.(*configFileRegistryBuilder).findOrAcquireConfigForFile(...)
...
github.com/microsoft/TypeScript/tsc/internal/project.(*ProjectCollectionBuilder).findOrCreateDefaultConfiguredProjectWorker.func2(...)
tsc/internal/project/projectcollectionbuilder.go:1108
acquireConfigForFile does entry, _ := c.configs.LoadOrStore(...) and immediately calls entry.ChangeIf(...). When dirty.SyncMap.LoadOrStore returns (nil, false) for a tombstoned key (after closeProjects deleted the configured project / config retention), entry is nil and ChangeIf crashes.
🙂 Expected behavior
The update succeeds: the project is closed and the file is (re)opened, either under a recreated configured project or the inferred project — without killing the server.
For comparison, these paths do not panic on the same build:
closeProjects+openProjectsin the same update (open cancels the pending close)closeProjects, then a later update with onlyopenProjects(noopenFiles)
Additional information about the issue
Matrix on 7.1.0-dev.20261030.1 (Windows x64), starting from createSnapshot({ openProjects, openFiles }):
| Update | Result |
|---|---|
{ closeProjects, openFiles } same request |
panic |
{ closeProjects } then { openFiles } |
panic |
{ closeProjects } then { openProjects, openFiles } |
panic |
{ closeProjects, openProjects } same request |
OK |
{ closeProjects, openProjects, openFiles } same request |
OK |
{ closeProjects } then { openProjects } only |
OK |
Same acquireConfigForFile / nil-ChangeIf frame as #65054, but the trigger here is closing a configured project that had open files and then opening files again. #64911 is the synthetic-program sibling of the same LoadOrStore-after-tombstone pattern.
Activity