{"record":{"id":"2e0b6a4043d24351","repo":"transloadit/uppy","slug":"failed-to-get-canvas-context","errorCode":null,"errorMessage":"Failed to get canvas context","messagePattern":"Failed to get canvas context","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/@uppy/screen-capture/src/ScreenCapture.tsx","lineNumber":566,"sourceCode":"      }\n\n      const video = document.createElement('video')\n      video.srcObject = stream\n\n      await new Promise((resolve) => {\n        video.onloadedmetadata = () => {\n          video.play()\n          resolve(null)\n        }\n      })\n\n      const canvas = document.createElement('canvas')\n      canvas.width = video.videoWidth\n      canvas.height = video.videoHeight\n\n      const ctx = canvas.getContext('2d')\n      if (!ctx) {\n        throw new Error('Failed to get canvas context')\n      }\n\n      ctx.drawImage(video, 0, 0, canvas.width, canvas.height)\n\n      // Validate and set fallback for preferred image mime type\n      let mimeType = this.opts.preferredImageMimeType\n      if (!mimeType || !SUPPORTED_IMAGE_TYPES.includes(mimeType)) {\n        this.uppy.log(\n          `Unsupported image type \"${mimeType}\", falling back to image/png`,\n          'warning',\n        )\n        mimeType = 'image/png'\n      }\n\n      const quality = 1\n\n      return new Promise((resolve, reject) => {\n        canvas.toBlob(","sourceCodeStart":548,"sourceCodeEnd":584,"githubUrl":"https://github.com/transloadit/uppy/blob/5d4dedd02a1ac0ae022c75c54aca76558f88e256/packages/@uppy/screen-capture/src/ScreenCapture.tsx#L548-L584","documentation":"captureScreenshot creates an offscreen canvas and throws when canvas.getContext('2d') returns null. Browsers return null for a 2d context when the canvas has zero or invalid dimensions — here video.videoWidth/videoHeight are 0 because the video element has no loaded metadata yet.","triggerScenarios":"Calling captureScreenshot before the temporary video element fired loadedmetadata/loadeddata, so videoWidth/videoHeight are 0 and the context cannot be created (spec: getContext returns null if width/height are 0 in some engines, or the context is otherwise unavailable).","commonSituations":"Slow source warm-up, a stream with an ended/inactive video track, GPU/canvas context exhaustion, or browser quirks when the video was never played/muted to kick off decoding.","solutions":["Retry the screenshot after a short delay or after the stream's video track is delivering frames (requestVideoFrameCallback)","Ensure the video element is muted and play() is awaited before capture","Verify the stream's track is live (track.readyState === 'live') before capturing"],"exampleFix":"// before\nvideo.srcObject = stream\n// immediately draw -> canvas.width = 0 -> ctx null\n\n// after\nvideo.srcObject = stream\nawait new Promise((r) => { video.onloadedmetadata = r })\nawait video.play()\nif (video.videoWidth === 0) throw new Error('No frames yet')","handlingStrategy":"retry","validationCode":"if (videoRef.videoWidth === 0 || videoRef.videoHeight === 0) {\n  // wait for frames before allowing screenshot\n}","typeGuard":"const hasVideoFrames = (v: HTMLVideoElement): boolean => v.readyState >= 2 && v.videoWidth > 0","tryCatchPattern":"try { await plugin.captureScreenshot() } catch (e) { if (e.message === 'Failed to get canvas context') setTimeout(() => plugin.captureScreenshot(), 500) }","preventionTips":["Await video metadata/first frame before capture","Verify stream tracks are live before screenshotting","Retry once after a short delay — often a warm-up race"],"tags":["canvas","video-metadata","screen-capture","race-condition"],"backgroundTag":"canvas-context-null","analyzedSha":"5d4dedd02a1ac0ae022c75c54aca76558f88e256","analyzedAt":"2026-08-28T12:18:41.267Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}