{"record":{"id":"7f8baea3f64a5e09","repo":"moeru-ai/airi","slug":"failed-to-create-canvas-context","errorCode":null,"errorMessage":"Failed to create canvas context","messagePattern":"Failed to create canvas context","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/stage-tamagotchi/src/renderer/composables/use-vision-screen-capture.ts","lineNumber":171,"sourceCode":"  }\n\n  function captureFrame(video: HTMLVideoElement, quality = 0.82, maxWidth = 1280, maxHeight = 720) {\n    if (!video || video.readyState < 2)\n      return null\n\n    const canvas = document.createElement('canvas')\n    const sourceWidth = video.videoWidth\n    const sourceHeight = video.videoHeight\n    if (sourceWidth <= 0 || sourceHeight <= 0)\n      return null\n\n    const scale = Math.min(maxWidth / sourceWidth, maxHeight / sourceHeight, 1)\n    canvas.width = Math.round(sourceWidth * scale)\n    canvas.height = Math.round(sourceHeight * scale)\n\n    const ctx = canvas.getContext('2d')\n    if (!ctx)\n      throw new Error('Failed to create canvas context')\n\n    ctx.drawImage(video, 0, 0, canvas.width, canvas.height)\n    return canvas.toDataURL('image/jpeg', quality)\n  }\n\n  return {\n    sources,\n    activeSourceId,\n    activeSource,\n    activeStream,\n    isRefetching,\n    hasFetchedOnce,\n    refetchSources,\n    startStream,\n    stopStream,\n    cleanup,\n    captureFrame,\n  }","sourceCodeStart":153,"sourceCodeEnd":189,"githubUrl":"https://github.com/moeru-ai/airi/blob/27111382b4a79a7e983289d6e983a06af185ed0f/apps/stage-tamagotchi/src/renderer/composables/use-vision-screen-capture.ts#L153-L189","documentation":"Thrown by the frame-capture helper when canvas.getContext('2d') returns null. The canvas was created and sized from the video dimensions, but the browser refused to hand back a 2D rendering context. This is rare — getContext('2d') returns null only under severe resource exhaustion or a corrupted canvas state.","triggerScenarios":"GPU/context pool exhausted after many simultaneous canvases; the renderer process hit a hard memory ceiling; canvas was tainted or created during teardown; Chromium bug under headless/constrained GPU.","commonSituations":"Long-running capture sessions leaking canvases; very high frame-rate capture thrashing the GPU; running under a software-rendered/headless GPU; tab/process near OOM.","solutions":["Reduce concurrent capture canvases and reuse a single canvas where possible.","Free canvas references (set width/height to 0) after capture to release GPU memory.","Catch the null context and skip the frame instead of throwing, so the stream keeps running.","If persistent, check for GPU/process memory pressure in Electron's task manager."],"exampleFix":"// before\nconst ctx = canvas.getContext('2d')\nif (!ctx)\n  throw new Error('Failed to create canvas context')\n\n// after\nconst ctx = canvas.getContext('2d')\nif (!ctx)\n  return null\nctx.drawImage(video, 0, 0, canvas.width, canvas.height)","handlingStrategy":"fallback","validationCode":"function canAcquire2dContext(): boolean {\n  const probe = document.createElement('canvas')\n  probe.width = 1; probe.height = 1\n  return probe.getContext('2d') !== null\n}","typeGuard":"function has2dContext(canvas: HTMLCanvasElement): canvas is HTMLCanvasElement & { getContext(c: '2d'): CanvasRenderingContext2D } {\n  return canvas.getContext('2d') !== null\n}","tryCatchPattern":null,"preventionTips":["Reuse a single canvas across frames instead of allocating per capture.","Set canvas width/height to 0 when done to release GPU memory.","Return null (skip frame) instead of throwing when context acquisition fails."],"tags":["screen-capture","canvas","gpu","resource-exhaustion"],"backgroundTag":null,"analyzedSha":"27111382b4a79a7e983289d6e983a06af185ed0f","analyzedAt":"2026-08-12T18:33:34.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}