microsoft/typescript-go · error · ErrClientError

%w: type handle %d not found in project registry

Error message

%w: type handle %d not found in project registry

What it means

The project's type registry exists but the TypeID is not in it: "type handle N not found in project registry". Because type ids are per-checker sequential values, the numeric id can exist in one project's registry and not another's - this error is the signature of a scope mismatch (id from a different project, a different snapshot of the same project, or a stale registry after release/update) rather than a corrupt id.

Source

Thrown at internal/api/session.go:274

	}
	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 {
		return nil, fmt.Errorf("%w: type handle %d not found in project registry", ErrClientError, handle)
	}

	return t, nil
}

// resolveSignatureHandle resolves a signature handle within the project's registry.
func (sd *snapshotData) resolveSignatureHandle(projectID ProjectID, handle SignatureID) (*checker.Signature, error) {
	if handle == 0 {
		return nil, fmt.Errorf("%w: empty signature handle", ErrClientError)
	}
	if projectID == "" {
		return nil, fmt.Errorf("%w: empty project ID for signature handle %d", ErrClientError, handle)
	}

	sd.projectRegistriesMu.RLock()
	reg := sd.projectRegistries[projectID]
	sd.projectRegistriesMu.RUnlock()

View on GitHub (pinned to 1bcfa18d79)

Solutions

  1. Re-acquire the type in the current project/snapshot before the follow-up query
  2. Invalidate cached type handles whenever the snapshot id changes or release completes
  3. Bind each cached type id to its exact project and snapshot, and send both verbatim

Example fix

// before
await call("getTypeParametersOfType", { snapshot, project, type: staleType }); // not found in registry

// after
const t = await call("getTypeAtPosition", { snapshot, project, file, position });
if (t) await call("getTypeParametersOfType", { snapshot, project, type: t.id });
Defensive patterns

Strategy: fallback

Validate before calling

// TS client: only use handles minted in the current snapshot.
if (cachedType.snapshot !== snapshot || cachedType.project !== project) {
  cachedType = null; // stale - force re-acquisition
}
if (!cachedType) {
  const t = await call("getTypeAtPosition", { snapshot, project, file, position });
  if (t) cachedType = { snapshot, project, id: t.id };
}

Type guard

const isLiveTypeHandle = (h: { snapshot: bigint; project: string; id: number }, cur: bigint): h is { snapshot: bigint; project: string; id: number } =>
  h.snapshot === cur && h.id > 0;

Try / catch

try {
  return await call(method, params);
} catch (e) {
  if (String(e).includes("type handle") && String(e).includes("not found")) {
    const t = await call("getTypeOfSymbol", { snapshot: params.snapshot, project: params.project, symbol });
    if (t) return await call(method, { ...params, type: t.id }); // retry once with fresh handle
  }
  throw e;
}

Prevention

When it happens

Trigger: Type handle from project A sent with project B (same number, different meaning); handle minted in a previous snapshot reused after updateSnapshot; snapshot released and its registry dropped, then queried; id from another session.

Common situations: Client-side caches of type ids outliving a snapshot refresh; multi-project editors forwarding a hover type from one project into completion queries for another; background workers processing stale queues after an edit.

Related errors


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