microsoft/typescript-go · error · ErrClientError
%w: empty type handle
Error message
%w: empty type handle
What it means
A type-based request passed TypeID 0 ("empty type handle"). TypeID 0 is the zero value and never maps to a registered checker.Type, so resolveTypeHandle rejects it before any registry lookup. It almost always means the client sent a default-initialized field or took the id from a null TypeResponse (type-returning calls return null when there is nothing to report, e.g. getTypeOfSymbol on a symbol with no type).
Source
Thrown at internal/api/session.go:255
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()
if reg == nil {
return nil, fmt.Errorf("%w: type handle %d not found (no registry for project %s)", ErrClientError, handle, projectID)
}
reg.typeRegistryMu.RLock()
t, ok := reg.typeRegistry[handle]
reg.typeRegistryMu.RUnlock()
if !ok {View on GitHub (pinned to 1bcfa18d79)
Solutions
- Null-check type responses before chaining further type queries
- Validate the handle is a positive integer before sending; skip or omit the call when it is 0
- Model type handles as optional in the client so absence is explicit rather than coerced to 0
Example fix
// before
const t = await call("getTypeOfSymbol", { snapshot, project, symbol });
await call("getTargetOfType", { snapshot, project, type: t?.id ?? 0 }); // empty type handle
// after
const t = await call("getTypeOfSymbol", { snapshot, project, symbol });
if (t) {
await call("getTargetOfType", { snapshot, project, type: t.id });
} Defensive patterns
Strategy: type-guard
Validate before calling
// TS client: skip the follow-up when the type handle is empty.
if (!typeId) { // 0/undefined/null
return;
}
await call("getTargetOfType", { snapshot, project, type: typeId }); Type guard
const isNonEmptyTypeHandle = (id: number | undefined | null): id is number => typeof id === "number" && Number.isInteger(id) && id > 0;
Prevention
- Null-check TypeResponse before chaining getTargetOfType/getTypesOfType/etc.
- Keep type ids optional in client models; absence means 'skip', not 0
- Use a shared isNonEmptyTypeHandle guard in every request helper
When it happens
Trigger: Passing 0 in getSymbolOfType/getTargetOfType/getTypesOfType/getSignaturesOfType params; reading .type?.id of a response where the type was null and letting it default to 0; undefined type field serialized as 0; forwarding a handle before the originating query populated it.
Common situations: Client pipelines that chain type queries without null checks; optional type fields in client structs defaulting to zero; template request objects reused across queries.
Related errors
- %w: empty symbol handle
- %w: empty signature handle
- %w: empty project ID for type handle %d
- %w: type handle %d not found (no registry for project %s)
- %w: type handle %d not found in project registry
AI-assisted analysis of microsoft/typescript-go@1bcfa18d79 (2026-08-16).
Data as JSON: /api/errors/42a08d9a51675cd7.
Report an issue: GitHub.