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
- Upgrade SDK/server to matching versions so the canvas closed payload includes a valid instanceId.
- Investigate why the producer emitted an empty/missing instanceId if you control the server or bridge.
- As a mitigation, clear stale canvas state by re-syncing canvas state (e.g. reopen session or call the canvas listing API if available).
- 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
- Validate event payloads before acting on them
- Re-sync canvas state periodically to heal stale entries
- Pin matching SDK/server versions
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
- failed to deserialize session.canvas.opened payload
- Missing required field 'cwd' in SessionContext
- Missing required field 'isAuthenticated' in…
- Missing required field 'message' in StopError
- Missing required fields in GetStatusResponse: version=
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)