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

  1. Use the tsgo binary bundled with the client package (omit tsserverPath) so versions always match
  2. Recreate the API/snapshot after project configuration changes instead of reusing old snapshots
  3. 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

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


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