microsoft/typescript-go · error · Error
Snapshot is disposed
Error message
Snapshot is disposed
What it means
Thrown by Snapshot.ensureNotDisposed() and surfaced from getProjects(), getProject(), and getDefaultProjectForFile() after dispose() ran. dispose() sets disposed=true, clears the project map and object registry, and sends a release request for the server-side snapshot; any later use of that Snapshot object throws immediately.
Source
Thrown at _packages/native-preview/src/api/async/api.ts:437
async dispose(): Promise<void> {
if (this.disposed) return;
this.disposed = true;
for (const project of this.projectMap.values()) {
project.dispose();
}
this.projectMap.clear();
this.snapshotRegistry.clear();
this.onDispose();
await this.client.apiRequest("release", { snapshot: this.id });
}
isDisposed(): boolean {
return this.disposed;
}
private ensureNotDisposed(): void {
if (this.disposed) {
throw new Error("Snapshot is disposed");
}
}
}
class SnapshotObjectRegistry {
private readonly symbols: Map<number, Symbol> = new Map();
private readonly client: Client;
private readonly snapshotId: number;
private readonly resolveProject: (projectId: Path) => Project | undefined;
constructor(client: Client, snapshotId: number, resolveProject: (projectId: Path) => Project | undefined) {
this.client = client;
this.snapshotId = snapshotId;
this.resolveProject = resolveProject;
}
/** Resolve a project id (a config file path) to its Project within this snapshot. */
getProject(projectId: Path): Project | undefined {
View on GitHub (pinned to 1bcfa18d79)
Solutions
- Reacquire state: call api.updateSnapshot() again and query the new Snapshot instead of the disposed one
- Guard every use with snapshot.isDisposed() (public) and refresh when true
- Never leak the temporary snapshot from runWithTemporaryFileUpdate's callback - extract plain data before it returns
- Tear down dependents in the same finally that disposes the snapshot so nothing queries it afterwards
Example fix
// before
const projects = snapshot.getProjects(); // throws if disposed earlier
// after
if (snapshot.isDisposed()) {
snapshot = await api.updateSnapshot(); // reacquire fresh state
}
const projects = snapshot.getProjects(); Defensive patterns
Strategy: validation
Validate before calling
if (snapshot.isDisposed()) {
snapshot = await api.updateSnapshot(); // reacquire before querying
}
const projects = snapshot.getProjects(); Try / catch
try {
return snapshot.getDefaultProjectForFile(file);
} catch (e) {
if (e instanceof Error && e.message === 'Snapshot is disposed') {
const fresh = await api.updateSnapshot();
return fresh.getDefaultProjectForFile(file); // retry on fresh state
}
throw e;
} Prevention
- Treat snapshots as single-transaction data: query immediately after acquiring
- Design async flows so nothing dereferences a snapshot after its dispose site
- On API.close(), drop every snapshot reference you hold
When it happens
Trigger: Holding a Snapshot across a subsequent updateSnapshot and continuing to query the old one after disposing it; using the temporary snapshot handed to a runWithTemporaryFileUpdate callback outside/after the callback (it is disposed in the finally block); touching snapshots after API.close(); double-lifecycle where app code disposed the snapshot then kept references.
Common situations: Editor-tooling loops that cache project lists from an old snapshot; async code that awaits past a dispose boundary (file closed, server session ended) and then calls getDefaultProjectForFile; tests reusing one snapshot for many mutations.
Related errors
- Cannot run a temporary file update on an inactive snapshot
- Cannot create directory: a file already exists at "/${segmen
- Invalid file path: "${path}"
- Bad line number. Line: ${line}, lineStarts.length: ${lineSta
- Cannot run a temporary file update on an inactive snapshot
AI-assisted analysis of microsoft/typescript-go@1bcfa18d79 (2026-08-16).
Data as JSON: /api/errors/fbcf44ed0b4adaed.
Report an issue: GitHub.