{"record":{"id":"d352522f9557a41a","repo":"github/copilot-sdk","slug":"failed-to-deserialize-session-canvas-closed-payloa","errorCode":null,"errorMessage":"failed to deserialize session.canvas.closed payload","messagePattern":"failed to deserialize session\\.canvas\\.closed payload","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"nodejs/src/session.ts","lineNumber":1088,"sourceCode":"        }\n    }\n\n    private upsertOpenCanvasFromEvent(data: unknown): void {\n        if (!isOpenCanvasInstance(data)) {\n            console.warn(\"failed to deserialize session.canvas.opened payload\");\n            return;\n        }\n        this.upsertOpenCanvas(data);\n    }\n\n    private removeOpenCanvasFromEvent(data: unknown): void {\n        if (\n            !data ||\n            typeof data !== \"object\" ||\n            typeof (data as { instanceId?: unknown }).instanceId !== \"string\" ||\n            (data as { instanceId: string }).instanceId.length === 0\n        ) {\n            console.warn(\"failed to deserialize session.canvas.closed payload\");\n            return;\n        }\n        this.removeOpenCanvas((data as { instanceId: string }).instanceId);\n    }\n\n    private removeOpenCanvas(instanceId: string): void {\n        this.openCanvasInstances = this.openCanvasInstances.filter(\n            (open) => open.instanceId !== instanceId\n        );\n    }\n\n    private upsertOpenCanvas(instance: OpenCanvasInstance): void {\n        const index = this.openCanvasInstances.findIndex(\n            (open) => open.instanceId === instance.instanceId\n        );\n        if (index >= 0) {\n            this.openCanvasInstances[index] = instance;\n        } else {","sourceCodeStart":1070,"sourceCodeEnd":1106,"githubUrl":"https://github.com/github/copilot-sdk/blob/cd8cf15dc3f9e762615790aaed0a771a0f392755/nodejs/src/session.ts#L1070-L1106","documentation":"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).","triggerScenarios":"Server sends a session.canvas.closed event with a missing, non-string, or empty instanceId field, so removeOpenCanvas cannot identify which canvas to remove.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":"function hasValidInstanceId(d: unknown): boolean {\n  return typeof (d as any)?.instanceId === 'string' && (d as any).instanceId.length > 0;\n}","typeGuard":"function isCanvasClosedPayload(v: unknown): v is { instanceId: string } {\n  return (\n    typeof v === 'object' &&\n    v !== null &&\n    typeof (v as { instanceId?: unknown }).instanceId === 'string' &&\n    (v as { instanceId: string }).instanceId.length > 0\n  );\n}","tryCatchPattern":"if (isCanvasClosedPayload(event.data)) {\n  untrackCanvas(event.data.instanceId);\n} else {\n  console.warn('skipping malformed canvas.closed payload', event.data);\n  scheduleCanvasStateResync();\n}","preventionTips":["Validate event payloads before acting on them","Re-sync canvas state periodically to heal stale entries","Pin matching SDK/server versions"],"tags":["deserialization","canvas","schema","event"],"backgroundTag":"unexpected-response-shape","analyzedSha":"cd8cf15dc3f9e762615790aaed0a771a0f392755","analyzedAt":"2026-09-09T18:32:31.973Z","contentChangedAt":"2026-09-09T18:32:31.973Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}