microsoft/typescript-go · error

Snapshot is disposed

Error message

Snapshot is disposed

What it means

Snapshot.ensureNotDisposed guards every operation on a Snapshot object. Once dispose() runs — explicitly, via API.close(), or because the snapshot was superseded — the object releases its server-side resources (apiRequest('release')) and refuses further use by throwing 'Snapshot is disposed'. This is a use-after-free guard for the RPC-backed snapshot lifecycle.

Source

Thrown at _packages/native-preview/src/api/sync/api.ts:445

    dispose(): 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();
        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

  1. Call snapshot.isDisposed() before each use and refetch a current snapshot when disposed
  2. Treat snapshots as short-lived: acquire, query, dispose — never cache across sync cycles
  3. Audit dispose() call sites so only the snapshot's owner disposes it

Example fix

// before
const info = snapshot.getQuickInfo(pos); // throws if disposed

// after
if (snapshot.isDisposed()) snapshot = api.getProjectSnapshot(project);
const info = snapshot.getQuickInfo(pos);
Defensive patterns

Strategy: validation

Validate before calling

if (snapshot.isDisposed()) { snapshot = api.getProjectSnapshot("tsconfig.json"); }

Try / catch

try { result = snapshot.someQuery(pos); } catch (e) { if ((e as Error).message === "Snapshot is disposed") { snapshot = refreshSnapshot(); result = snapshot.someQuery(pos); } else throw e; }

Prevention

When it happens

Trigger: Calling any method on a Snapshot after snapshot.dispose(); calling methods on the latest snapshot after api.close() disposed all snapshots; using a snapshot captured before an updateFiles/sync cycle replaced it.

Common situations: Long-lived editor integrations caching snapshots across document changes; error paths that dispose then continue processing; concurrent handlers sharing a Snapshot without coordinating disposal.

Related errors


AI-assisted analysis of microsoft/typescript-go@1bcfa18d79 (2026-08-16). Data as JSON: /api/errors/5c9b0df0e34c3d27. Report an issue: GitHub.