microsoft/typescript-go · error · ErrClientError
%w: symbol handle %d not found in snapshot registry
Error message
%w: symbol handle %d not found in snapshot registry
What it means
The SymbolID is non-zero but absent from this snapshot's symbolRegistry, so resolveSymbolHandle fails with "symbol handle N not found in snapshot registry". Symbols are registered snapshot-wide (ids come from a global counter, so the same symbol keeps one id), but the registry itself lives and dies with the snapshotData: once that snapshot is released or replaced, all its symbol handles become unresolvable.
Source
Thrown at internal/api/session.go:246
}
return id
}
reg.typeRegistry[id] = t
return id
}
// resolveSymbolHandle resolves a symbol handle within the snapshot's registry.
func (sd *snapshotData) resolveSymbolHandle(handle SymbolID) (*ast.Symbol, error) {
if handle == 0 {
return nil, fmt.Errorf("%w: empty symbol handle", ErrClientError)
}
sd.symbolRegistryMu.RLock()
symbol, ok := sd.symbolRegistry[handle]
sd.symbolRegistryMu.RUnlock()
if !ok {
return nil, fmt.Errorf("%w: symbol handle %d not found in snapshot registry", ErrClientError, handle)
}
return symbol, nil
}
// resolveTypeHandle resolves a type handle within the project's registry.
func (sd *snapshotData) resolveTypeHandle(projectID ProjectID, handle TypeID) (*checker.Type, error) {
if handle == 0 {
return nil, fmt.Errorf("%w: empty type handle", ErrClientError)
}
if projectID == "" {
return nil, fmt.Errorf("%w: empty project ID for type handle %d", ErrClientError, handle)
}
sd.projectRegistriesMu.RLock()
reg := sd.projectRegistries[projectID]
sd.projectRegistriesMu.RUnlock()
View on GitHub (pinned to 1bcfa18d79)
Solutions
- Re-acquire the symbol in the current snapshot (e.g. getSymbolAtPosition/getSymbolAtLocation) instead of reusing an old handle
- Invalidate all cached symbol handles whenever updateSnapshot returns a new snapshot id or release completes
- Keep handles paired with the snapshot id they came from and always send that snapshot in follow-ups
Example fix
// before
const symId = cache.get("hoverSymbol"); // from snapshot N-1
await call("getMembersOfSymbol", { snapshot: current, project, symbol: symId }); // not found
// after
const sym = await call("getSymbolAtPosition", { snapshot: current, project, file, position });
if (sym) await call("getMembersOfSymbol", { snapshot: current, project, symbol: sym.id }); Defensive patterns
Strategy: fallback
Validate before calling
// TS client: never send a symbol handle from another snapshot.
function symbolUsableIn(handle: {snapshot: bigint, id: number}, current: bigint): boolean {
return handle.snapshot === current;
}
if (!symbolUsableIn(cached, snapshot)) {
cached = await requerySymbolAtPosition(file, position); // refresh in current snapshot
} Type guard
type SymbolRef = { snapshot: bigint; project: string; id: number };
const isResolvableSymbolRef = (r: SymbolRef | null | undefined, current: bigint): r is SymbolRef =>
!!r && typeof r.id === "number" && r.id > 0 && r.snapshot === current; Try / catch
try {
return await call(method, params);
} catch (e) {
if (String(e).includes("not found in snapshot registry")) {
// handle is stale for this snapshot: re-acquire the symbol at its location and retry once
const sym = await call("getSymbolAtPosition", { snapshot: params.snapshot, project: params.project, file, position });
if (sym) return await call(method, { ...params, symbol: sym.id });
}
throw e;
} Prevention
- Store {snapshot, project, id} together for every symbol you keep
- Drop all symbol caches when updateSnapshot returns a new id or release completes
- Re-acquire symbols at their source location instead of trusting long-lived ids in editor state
When it happens
Trigger: Using a SymbolID obtained from snapshot A in a request against snapshot B (before or after an updateSnapshot); calling release for the snapshot and then re-using its handles; ids carried over after a server restart (the counter and registry reset); ids from a different session/process; fabricated ids.
Common situations: Client caches symbols in editor decorations/hover state across document edits (each updateSnapshot mints a new snapshot); long-lived background analyzers holding symbol ids; session restart invalidating every stored handle.
Related errors
- %w: type handle %d not found (no registry for project %s)
- %w: type handle %d not found in project registry
- %w: signature handle %d not found (no registry for project %
- %w: signature handle %d not found in project registry
- %w: snapshot %d not found
AI-assisted analysis of microsoft/typescript-go@1bcfa18d79 (2026-08-16).
Data as JSON: /api/errors/5f2b71e929585aeb.
Report an issue: GitHub.