mihomo-party-org/clash-party · error · Error
Failed to get 2D context
Error message
Failed to get 2D context
What it means
cropAndPadTransparent() crops an image to its non-transparent bounding box and pads it into a square using a hidden canvas. It calls canvas.getContext('2d') on the source canvas and throws 'Failed to get 2D context' if the browser returns null. getContext returns null when the 2D context cannot be created, which is rare but possible.
Source
Thrown at src/renderer/src/utils/image.ts:18
export async function cropAndPadTransparent(
base64: string,
finalSize = 256,
border = 24
): Promise<string> {
const img = new Image()
img.src = base64
await new Promise((resolve, reject) => {
img.onload = resolve
img.onerror = reject
})
const canvas = document.createElement('canvas')
canvas.width = img.width
canvas.height = img.height
const ctx = canvas.getContext('2d')
if (!ctx) {
throw new Error('Failed to get 2D context')
}
ctx.clearRect(0, 0, canvas.width, canvas.height)
ctx.drawImage(img, 0, 0)
const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height)
const { data, width, height } = imgData
let top = height,
bottom = 0,
left = width,
right = 0
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const i = (y * width + x) * 4 + 3
if (data[i] > 10) {
if (x < left) left = x
if (x > right) right = x
if (y < top) top = yView on GitHub (pinned to 911e090537)
Solutions
- Guard the call: only invoke after img.complete && img.naturalWidth > 0 (await decode() first).
- If dimensions may be huge, cap them (downscale first) so canvas allocation succeeds.
- If using an ImageBitmap/SVG, render it onto an intermediate canvas with explicit width/height before cropping.
- Keep the try-catch and surface a user-facing 'image could not be processed' message instead of crashing the flow.
Example fix
// before
const ctx = canvas.getContext('2d')
if (!ctx) throw new Error('Failed to get 2D context')
// after
await img.decode()
if (!img.naturalWidth || !img.naturalHeight) throw new Error('Image has no size')
const ctx = canvas.getContext('2d', { willReadFrequently: true })
if (!ctx) throw new Error('Failed to get 2D context') Defensive patterns
Strategy: type-guard
Validate before calling
if (!img.complete || !img.naturalWidth || !img.naturalHeight) {
throw new Error('Image not loaded or has zero size')
}
if (img.naturalWidth * img.naturalHeight > 4096 * 4096) {
throw new Error('Image too large for canvas processing')
} Type guard
function isDecodedImage(img: HTMLImageElement): img is HTMLImageElement & { naturalWidth: number; naturalHeight: number } {
return img.complete && img.naturalWidth > 0 && img.naturalHeight > 0
} Try / catch
try {
const result = await cropAndPadTransparent(img)
} catch (e) {
if (e.message === 'Failed to get 2D context') {
// fall back to showing the original image unprocessed
} else {
throw e
}
} Prevention
- Await img.decode() (or onload) before canvas operations.
- Check naturalWidth/naturalHeight > 0 before processing.
- Downscale very large images before canvas cropping to respect browser canvas limits.
- Handle promise rejections at call sites instead of letting them bubble into unhandled rejections.
When it happens
Trigger: Calling cropAndPadTransparent with an <img> whose width or height is 0 (e.g. image not loaded or broken src), so the canvas gets a 0-sized dimension and the 2D context cannot be instantiated; browser/hardware failing to allocate a canvas backbuffer (huge width*height from a corrupt or enormous image); hostile environments where getContext('2d') is stubbed to null.
Common situations: Pasting/processing an image before its onload fired so img.width is 0; an animated or SVG image without intrinsic size; very large screenshots (e.g. 30000px wide) exhausting GPU memory; WebView with accelerated canvas disabled.
AI-assisted analysis of mihomo-party-org/clash-party@911e090537 (2026-08-30).
Data as JSON: /api/errors/4a159fd884d42443.
Report an issue: GitHub.