github/copilot-sdk · warning

failed to deserialize session.canvas.closed payload

Error message

failed to deserialize session.canvas.closed payload

What it means

When a session.canvas.closed event arrives, CopilotSession validates that the payload contains a non-empty string instanceId before removing the canvas from its open-canvas tracking. If validation fails, it logs this warning and leaves the canvas marked as open (stale state).

Solutions

  1. Upgrade SDK/server to matching versions so the canvas closed payload includes a valid instanceId.
  2. Investigate why the producer emitted an empty/missing instanceId if you control the server or bridge.
  3. As a mitigation, clear stale canvas state by re-syncing canvas state (e.g. reopen session or call the canvas listing API if available).
  4. Log the raw payload alongside the warning to capture the malformed event for diagnosis.
Defensive patterns

Strategy: type-guard

Validate before calling

function hasValidInstanceId(d: unknown): boolean {
  return typeof (d as any)?.instanceId === 'string' && (d as any).instanceId.length > 0;
}

Type guard

function isCanvasClosedPayload(v: unknown): v is { instanceId: string } {
  return (
    typeof v === 'object' &&
    v !== null &&
    typeof (v as { instanceId?: unknown }).instanceId === 'string' &&
    (v as { instanceId: string }).instanceId.length > 0
  );
}

Try / catch

if (isCanvasClosedPayload(event.data)) {
  untrackCanvas(event.data.instanceId);
} else {
  console.warn('skipping malformed canvas.closed payload', event.data);
  scheduleCanvasStateResync();
}

Prevention

When it happens

Trigger: Server sends a session.canvas.closed event with a missing, non-string, or empty instanceId field, so removeOpenCanvas cannot identify which canvas to remove.

Common situations: Server/client version mismatch on the canvas event schema; a server bug emitting an empty instanceId; event payloads altered by intermediate proxies or custom transports.

Related errors


AI-assisted analysis of github/copilot-sdk@cd8cf15dc3 (2026-09-09). Data as JSON: /api/errors/d352522f9557a41a. Report an issue: GitHub.

Appendix: source

Thrown at nodejs/src/session.ts:1088

        }
    }

    private upsertOpenCanvasFromEvent(data: unknown): void {
        if (!isOpenCanvasInstance(data)) {
            console.warn("failed to deserialize session.canvas.opened payload");
            return;
        }
        this.upsertOpenCanvas(data);
    }

    private removeOpenCanvasFromEvent(data: unknown): void {
        if (
            !data ||
            typeof data !== "object" ||
            typeof (data as { instanceId?: unknown }).instanceId !== "string" ||
            (data as { instanceId: string }).instanceId.length === 0
        ) {
            console.warn("failed to deserialize session.canvas.closed payload");
            return;
        }
        this.removeOpenCanvas((data as { instanceId: string }).instanceId);
    }

    private removeOpenCanvas(instanceId: string): void {
        this.openCanvasInstances = this.openCanvasInstances.filter(
            (open) => open.instanceId !== instanceId
        );
    }

    private upsertOpenCanvas(instance: OpenCanvasInstance): void {
        const index = this.openCanvasInstances.findIndex(
            (open) => open.instanceId === instance.instanceId
        );
        if (index >= 0) {
            this.openCanvasInstances[index] = instance;
        } else {

View on GitHub (pinned to cd8cf15dc3)