{"record":{"id":"fbcf44ed0b4adaed","repo":"microsoft/typescript-go","slug":"snapshot-is-disposed","errorCode":null,"errorMessage":"Snapshot is disposed","messagePattern":"Snapshot is disposed","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"_packages/native-preview/src/api/async/api.ts","lineNumber":437,"sourceCode":"    async dispose(): Promise<void> {\r\n        if (this.disposed) return;\r\n        this.disposed = true;\r\n        for (const project of this.projectMap.values()) {\r\n            project.dispose();\r\n        }\r\n        this.projectMap.clear();\r\n        this.snapshotRegistry.clear();\r\n        this.onDispose();\r\n        await this.client.apiRequest(\"release\", { snapshot: this.id });\r\n    }\r\n\r\n    isDisposed(): boolean {\r\n        return this.disposed;\r\n    }\r\n\r\n    private ensureNotDisposed(): void {\r\n        if (this.disposed) {\r\n            throw new Error(\"Snapshot is disposed\");\r\n        }\r\n    }\r\n}\r\n\r\nclass SnapshotObjectRegistry {\r\n    private readonly symbols: Map<number, Symbol> = new Map();\r\n    private readonly client: Client;\r\n    private readonly snapshotId: number;\r\n    private readonly resolveProject: (projectId: Path) => Project | undefined;\r\n\r\n    constructor(client: Client, snapshotId: number, resolveProject: (projectId: Path) => Project | undefined) {\r\n        this.client = client;\r\n        this.snapshotId = snapshotId;\r\n        this.resolveProject = resolveProject;\r\n    }\r\n\r\n    /** Resolve a project id (a config file path) to its Project within this snapshot. */\r\n    getProject(projectId: Path): Project | undefined {\r","sourceCodeStart":419,"sourceCodeEnd":455,"githubUrl":"https://github.com/microsoft/typescript-go/blob/1bcfa18d79a3be41772223d5c05dfe4480e614ff/_packages/native-preview/src/api/async/api.ts#L419-L455","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nconst projects = snapshot.getProjects(); // throws if disposed earlier\n\n// after\nif (snapshot.isDisposed()) {\n    snapshot = await api.updateSnapshot(); // reacquire fresh state\n}\nconst projects = snapshot.getProjects();","handlingStrategy":"validation","validationCode":"if (snapshot.isDisposed()) {\n    snapshot = await api.updateSnapshot(); // reacquire before querying\n}\nconst projects = snapshot.getProjects();","typeGuard":null,"tryCatchPattern":"try {\n    return snapshot.getDefaultProjectForFile(file);\n} catch (e) {\n    if (e instanceof Error && e.message === 'Snapshot is disposed') {\n        const fresh = await api.updateSnapshot();\n        return fresh.getDefaultProjectForFile(file); // retry on fresh state\n    }\n    throw e;\n}","preventionTips":["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"],"tags":["api","snapshot","lifecycle","native-preview","validation"],"backgroundTag":null,"analyzedSha":"1bcfa18d79a3be41772223d5c05dfe4480e614ff","analyzedAt":"2026-08-16T02:12:00.115Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}