microsoft/typescript-go · error · ErrClientError

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

Error message

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

What it means

The project's signature registry exists but the SignatureID is not registered in it: "signature handle N not found in project registry". Signature ids are per-checker sequential, so the same number can legitimately belong to different signatures in different projects/snapshots - this error indicates a scope mismatch (stale snapshot, different project, post-release registry) rather than an invalid id format.

Source

Thrown at internal/api/session.go:302

	}
	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()

	if reg == nil {
		return nil, fmt.Errorf("%w: signature handle %d not found (no registry for project %s)", ErrClientError, handle, projectID)
	}

	reg.signatureRegistryMu.RLock()
	sig, ok := reg.signatureRegistry[handle]
	reg.signatureRegistryMu.RUnlock()

	if !ok {
		return nil, fmt.Errorf("%w: signature handle %d not found in project registry", ErrClientError, handle)
	}

	return sig, nil
}

// newSignatureResponse registers a signature in the project's registry and returns the response.
func (sd *snapshotData) newSignatureResponse(projectID ProjectID, sig *checker.Signature) *SignatureResponse {
	if sig == nil {
		return nil
	}
	resp := &SignatureResponse{
		Id:    sd.registerSignature(projectID, sig),
		Flags: uint32(sig.Flags()),
	}

	if sig.Declaration() != nil {
		resp.Declaration = sd.nodeHandleFrom(sig.Declaration())
	}

View on GitHub (pinned to 1bcfa18d79)

Solutions

  1. Re-acquire the signature in the current snapshot before the follow-up call
  2. Bind cached signature handles to (snapshot, project) and invalidate on change/release
  3. Send the project exactly as returned with the signature originally

Example fix

// before
await call("signatureFollowUp", { snapshot, project, signature: staleSig }); // not found

// after
const sig = await call("getResolvedSignature", { snapshot, project, file, position });
if (sig) await call("signatureFollowUp", { snapshot, project, signature: sig.id });
Defensive patterns

Strategy: fallback

Validate before calling

// TS client: only use signature handles minted in the current snapshot.
if (cachedSig.snapshot !== snapshot || cachedSig.project !== project) {
  cachedSig = null;
}
if (!cachedSig) {
  const sig = await call("getResolvedSignature", { snapshot, project, file, position });
  if (sig) cachedSig = { snapshot, project, id: sig.id };
}

Type guard

const isLiveSignatureHandle = (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("signature handle") && String(e).includes("not found")) {
    const sig = await call("getResolvedSignature", { snapshot: params.snapshot, project: params.project, file, position });
    if (sig) return await call(method, { ...params, signature: sig.id }); // one retry with fresh handle
  }
  throw e;
}

Prevention

When it happens

Trigger: Reusing a signature handle after updateSnapshot produced a new snapshot; pairing a signature id with the wrong project; querying after release dropped the snapshot's registries; carrying handles across a server restart.

Common situations: Signature-help state cached across document edits; multi-project editors forwarding handles between projects; background workers draining stale request queues after an edit.

Related errors


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