microsoft/typescript-go · error · ErrClientError

%w: empty project ID for signature handle %d

Error message

%w: empty project ID for signature handle %d

What it means

A signature-based request supplied a SignatureID but no ProjectID, so resolveSignatureHandle fails with "empty project ID for signature handle N". Signature ids are sequential per checker (per project), so - exactly like type handles - a signature handle is only meaningful together with the project it was created in, and the project parameter is mandatory in every request that carries a signature handle.

Source

Thrown at internal/api/session.go:286

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

View on GitHub (pinned to 1bcfa18d79)

Solutions

  1. Always send the project that the signature handle came from
  2. Store {projectId, signatureId} as an atomic pair in client state
  3. If project is unknown, take it from the SymbolResponse.Project or the response that produced the signature

Example fix

// before
await call("getTargetOfSignature"-style follow-up, { snapshot, signature: sigId }); // empty project ID

// after
await call("someSignatureFollowUp", { snapshot, project: sigId.project, signature: sigId.id });
Defensive patterns

Strategy: validation

Validate before calling

// TS client: require project whenever a signature handle is sent.
function assertSignatureRequest(params: { project?: string; signature?: number }) {
  if (params.signature !== undefined) {
    if (!params.project) throw new Error("signature handle requires its originating project");
    if (!(params.signature > 0)) throw new Error("signature handle must be > 0");
  }
}

Type guard

const isProjectScopedSignatureHandle = (h: { project?: string; id?: number } | null | undefined): h is { project: string; id: number } =>
  !!h && typeof h.project === "string" && h.project.length > 0 && typeof h.id === "number" && h.id > 0;

Prevention

When it happens

Trigger: Omitting project from params when querying a signature's parameters, target, or declaration; a client struct defaulting project to ""; copying the signature id out of a SignatureResponse without its project context.

Common situations: Client helpers that forward handles without project state; refactor dropping the project field; assuming signatures are snapshot-global like symbols (they are project-scoped).

Related errors


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