microsoft/typescript-go · critical
Symbol ${data.id} references unknown canonical project '${da
Error message
Symbol ${data.id} references unknown canonical project '${data.project}' What it means
When the client materializes a Symbol from SymbolResponse, it resolves data.project to a canonical Project via the snapshot's registry. If the server references a project id the client has no record of, the Symbol cannot be tied to its project's NodeHandles and the client throws. This is a snapshot-integrity failure: the response stream assumes a project this client never received (or already dropped).
Source
Thrown at _packages/native-preview/src/api/sync/api.ts:1990
readonly checkFlags: CheckFlags;
readonly declarations: readonly NodeHandle<Declaration>[];
readonly valueDeclaration: NodeHandle<Declaration> | undefined;
private readonly parent!: number;
private readonly exportSymbol!: number;
private membersCache: ReadonlyMap<__String, Symbol> | undefined;
private exportsCache: ReadonlyMap<__String, Symbol> | undefined;
constructor(data: SymbolResponse, objectRegistry: SnapshotObjectRegistry) {
this.objectRegistry = objectRegistry;
this.id = data.id;
this.escapedName = data.name;
this.name = unescapeLeadingUnderscores(data.name);
this.flags = data.flags;
this.checkFlags = data.checkFlags;
const canonicalProject = objectRegistry.getProject(data.project);
if (!canonicalProject) {
throw new Error(`Symbol ${data.id} references unknown canonical project '${data.project}'`);
}
this.canonicalProject = canonicalProject;
this.declarations = (data.declarations ?? []).map(d => new NodeHandle<Declaration>(d, canonicalProject));
this.valueDeclaration = data.valueDeclaration ? new NodeHandle<Declaration>(data.valueDeclaration, canonicalProject) : undefined;
if (data.parent !== undefined) this.parent = data.parent;
if (data.exportSymbol !== undefined) this.exportSymbol = data.exportSymbol;
}
getParent(): Symbol | undefined {
return this.objectRegistry.fetchSymbol(this, "getParentOfSymbol", this.parent, this.canonicalProject.id);
}
/**
* Get this symbol's members keyed by escaped name. The result is cached on
* the symbol, so repeated calls do not round-trip to the server.
*/
getMembers(): ReadonlyMap<__String, Symbol> {
View on GitHub (pinned to 1bcfa18d79)
Solutions
- Use the tsgo binary bundled with the client package (omit tsserverPath) so versions always match
- Recreate the API/snapshot after project configuration changes instead of reusing old snapshots
- If it persists with matched versions, capture the failing symbol/project ids and report upstream
Defensive patterns
Strategy: try-catch
Try / catch
try { /* materialize symbol */ } catch (e) { if ((e as Error).message.includes("unknown canonical project")) { /* recreate API/snapshot; report version mismatch */ } else throw e; } Prevention
- Use the bundled tsgo binary (omit tsserverPath) to avoid version skew
- Recreate snapshots after project config changes
- Report persistent cases with the symbol and project ids
When it happens
Trigger: Symbol data arriving for a project that was never part of this snapshot's project map; snapshot state released mid-session; client and tsgo binary built from different protocol versions where project ids are assigned differently.
Common situations: Version skew between @typescript/native-preview and the spawned tsgo binary (custom tsserverPath pointing at another build); long sessions where projects are reconfigured and a stale snapshot is still queried; partial snapshot handoff bugs in the server.
Related errors
- ${method} returned null symbol for ${source.constructor.name
- ${method} returned no type for ${source.constructor.name} ${
- ${method} returned null signature for ${source.constructor.n
- name mismatch for response: expected `${method}`, got `${thi
- SyncRpcChannel: could not obtain pipe file descriptors.
AI-assisted analysis of microsoft/typescript-go@1bcfa18d79 (2026-08-16).
Data as JSON: /api/errors/d6fd0489c37ab0f6.
Report an issue: GitHub.