moeru-ai/airi · error · Error

Failed to create canvas context

Error message

Failed to create canvas context

What it means

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.

Source

Thrown at apps/stage-tamagotchi/src/renderer/composables/use-vision-screen-capture.ts:171

  }

  function captureFrame(video: HTMLVideoElement, quality = 0.82, maxWidth = 1280, maxHeight = 720) {
    if (!video || video.readyState < 2)
      return null

    const canvas = document.createElement('canvas')
    const sourceWidth = video.videoWidth
    const sourceHeight = video.videoHeight
    if (sourceWidth <= 0 || sourceHeight <= 0)
      return null

    const scale = Math.min(maxWidth / sourceWidth, maxHeight / sourceHeight, 1)
    canvas.width = Math.round(sourceWidth * scale)
    canvas.height = Math.round(sourceHeight * scale)

    const ctx = canvas.getContext('2d')
    if (!ctx)
      throw new Error('Failed to create canvas context')

    ctx.drawImage(video, 0, 0, canvas.width, canvas.height)
    return canvas.toDataURL('image/jpeg', quality)
  }

  return {
    sources,
    activeSourceId,
    activeSource,
    activeStream,
    isRefetching,
    hasFetchedOnce,
    refetchSources,
    startStream,
    stopStream,
    cleanup,
    captureFrame,
  }

View on GitHub (pinned to 27111382b4)

Solutions

  1. Reduce concurrent capture canvases and reuse a single canvas where possible.
  2. Free canvas references (set width/height to 0) after capture to release GPU memory.
  3. Catch the null context and skip the frame instead of throwing, so the stream keeps running.
  4. If persistent, check for GPU/process memory pressure in Electron's task manager.

Example fix

// before
const ctx = canvas.getContext('2d')
if (!ctx)
  throw new Error('Failed to create canvas context')

// after
const ctx = canvas.getContext('2d')
if (!ctx)
  return null
ctx.drawImage(video, 0, 0, canvas.width, canvas.height)
Defensive patterns

Strategy: fallback

Validate before calling

function canAcquire2dContext(): boolean {
  const probe = document.createElement('canvas')
  probe.width = 1; probe.height = 1
  return probe.getContext('2d') !== null
}

Type guard

function has2dContext(canvas: HTMLCanvasElement): canvas is HTMLCanvasElement & { getContext(c: '2d'): CanvasRenderingContext2D } {
  return canvas.getContext('2d') !== null
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of moeru-ai/airi@27111382b4 (2026-08-12). Data as JSON: /api/errors/7f8baea3f64a5e09. Report an issue: GitHub.