microsoft/typescript-go · error · ErrClientError
%w: empty symbol handle
Error message
%w: empty symbol handle
What it means
A symbol-based request passed SymbolID 0 ("empty symbol handle"). 0 is the zero value of SymbolID and is reserved: no symbol ever registers under it, so resolveSymbolHandle rejects it immediately as a client error. In practice 0 arrives from default-initialized params or from the id of a null SymbolResponse - when the server finds no symbol (e.g. getSymbolAtPosition on whitespace or a comment) it returns null, and reading .id of that yields 0/undefined.
Source
Thrown at internal/api/session.go:238
reg := sd.getOrCreateProjectRegistry(projectID)
reg.typeRegistryMu.Lock()
defer reg.typeRegistryMu.Unlock()
existing := reg.typeRegistry[id]
if existing != nil {
if existing != t {
panic("duplicate type")
}
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)
}View on GitHub (pinned to 1bcfa18d79)
Solutions
- Null-check symbol responses: only issue symbol-follow-up calls when the response object exists
- Guard every handle before sending: reject/omit the call when symbolId is 0, null, or undefined
- Treat 0 as 'absent' in your client model (make the field optional, not defaulted)
Example fix
// before
const sym = await call("getSymbolAtPosition", { snapshot, project, file, position });
await call("getTypeOfSymbol", { snapshot, project, symbol: sym?.id ?? 0 }); // empty symbol handle
// after
const sym = await call("getSymbolAtPosition", { snapshot, project, file, position });
if (sym) {
await call("getTypeOfSymbol", { snapshot, project, symbol: sym.id });
} Defensive patterns
Strategy: type-guard
Validate before calling
// TS client: check the handle before the request.
if (!symbolId) { // covers 0, undefined, null
return; // nothing found earlier - skip the follow-up call
}
await call("getTypeOfSymbol", { snapshot, project, symbol: symbolId }); Type guard
const isNonEmptySymbolHandle = (id: number | bigint | undefined | null): id is number => (typeof id === "number" || typeof id === "bigint") && id !== 0 && id > 0;
Prevention
- Treat a null SymbolResponse as 'no symbol' and short-circuit follow-up queries
- Model symbol ids as optional fields, never zero-defaulted numbers
- Centralize one guard (id > 0) used by every symbol-taking request helper
When it happens
Trigger: Passing a zero-valued symbol field in getTypeOfSymbol/getMembersOfSymbol/getParentOfSymbol params; reusing the id from a getSymbolAtPosition response that was null; JS undefined id coerced to 0 in a struct; forwarding symbol handles before the first symbol query populated them.
Common situations: Client skips the null check on symbol lookups at whitespace/keywords; optional-field handling that defaults handles to 0 instead of omitting the request; copy-pasting a follow-up query template without filling the symbol.
Related errors
- %w: empty type handle
- %w: empty signature handle
- %w: symbol handle %d not found in snapshot registry
- %w: empty project ID for type handle %d
- %w: empty project ID for signature handle %d
AI-assisted analysis of microsoft/typescript-go@1bcfa18d79 (2026-08-16).
Data as JSON: /api/errors/a3cbce62fbdb0860.
Report an issue: GitHub.