microsoft/typescript-go · error · ErrClientError

%w: empty signature handle

Error message

%w: empty signature handle

What it means

A signature-based request passed SignatureID 0 ("empty signature handle"). SignatureID 0 is the zero value and never maps to a registered checker.Signature; resolveSignatureHandle rejects it up front as a client error. Typically the client sent a default-initialized field or took the id from a null/absent signature (getSignaturesOfType returns no entries when the type has none, and getResolvedSignature can return null when no call target resolves).

Source

Thrown at internal/api/session.go:283

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

	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 {

View on GitHub (pinned to 1bcfa18d79)

Solutions

  1. Null-check signature responses and absent parameter lists before issuing follow-ups
  2. Validate the handle is a positive integer before sending; skip the call when 0
  3. Make signature handles optional in the client model so 'no signature' is distinct from 'signature 0'

Example fix

// before
const sig = await call("getResolvedSignature", { snapshot, project, file, position });
await call("getTypeOfSymbol", { snapshot, project, symbol: sig?.id ? 0 : 0 }); // empty handle

// after
const sig = await call("getResolvedSignature", { snapshot, project, file, position });
if (sig) {
  // sig.id is non-zero; use sig.parameters symbols etc.
}
Defensive patterns

Strategy: type-guard

Validate before calling

// TS client: skip follow-ups when the signature handle is empty.
if (!signatureId) { // 0/undefined/null
  return; // no signature resolved at this position
}
await call("signatureFollowUp", { snapshot, project, signature: signatureId });

Type guard

const isNonEmptySignatureHandle = (id: number | bigint | undefined | null): id is number =>
  (typeof id === "number" || typeof id === "bigint") && id !== 0 && id > 0;

Prevention

When it happens

Trigger: Passing 0 in getResolvedSignature follow-ups or signature-parameter queries; using the id of a null SignatureResponse (e.g. getResolvedSignature on a non-call expression); undefined signature field serialized as 0.

Common situations: Signature-help UIs chaining parameter/type queries without checking that a signature was found; optional fields defaulting to zero; reusing param templates.

Related errors


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