{"record":{"id":"7a1e25043dc36a9a","repo":"PaddlePaddle/PaddleOCR","slug":"failed-to-create-a-2d-canvas-context-in-the-ocr-wo","errorCode":null,"errorMessage":"Failed to create a 2D canvas context in the OCR worker.","messagePattern":"Failed to create a 2D canvas context in the OCR worker\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"paddleocr-js/packages/core/src/platform/worker.ts","lineNumber":22,"sourceCode":" */\n\nimport type { OpenCv, Mat } from \"@techstark/opencv-js\";\nimport type { SourceMatResult } from \"./browser\";\nimport { ensureServedFromHttp } from \"./browser\";\n\nexport interface WorkerSourcePayload {\n  kind: \"imageBitmap\";\n  imageBitmap: ImageBitmap;\n}\n\nfunction imageBitmapToImageData(imageBitmap: ImageBitmap): ImageData {\n  if (typeof OffscreenCanvas !== \"function\") {\n    throw new Error(\"Worker mode requires OffscreenCanvas support in this browser.\");\n  }\n  const canvas = new OffscreenCanvas(imageBitmap.width, imageBitmap.height);\n  const ctx = canvas.getContext(\"2d\", { willReadFrequently: true });\n  if (!ctx) {\n    throw new Error(\"Failed to create a 2D canvas context in the OCR worker.\");\n  }\n  ctx.drawImage(imageBitmap, 0, 0);\n  return ctx.getImageData(0, 0, imageBitmap.width, imageBitmap.height);\n}\n\nfunction imageDataToMat(cv: OpenCv, imageData: ImageData): Mat {\n  return cv.matFromArray(imageData.height, imageData.width, cv.CV_8UC4, imageData.data);\n}\n\nfunction isWorkerSourcePayload(source: unknown): source is WorkerSourcePayload {\n  if (typeof source !== \"object\" || source === null) return false;\n  const candidate = source as Record<string, unknown>;\n  return (\n    candidate[\"kind\"] === \"imageBitmap\" &&\n    typeof ImageBitmap !== \"undefined\" &&\n    candidate[\"imageBitmap\"] instanceof ImageBitmap\n  );\n}","sourceCodeStart":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/PaddlePaddle/PaddleOCR/blob/2661c7c0ef5c613e8f93c6e93b2e052399f0f854/paddleocr-js/packages/core/src/platform/worker.ts#L4-L40","documentation":"Thrown inside the OCR worker when an OffscreenCanvas is successfully created but getContext(\"2d\", { willReadFrequently: true }) returns null. This is rarer than the capability gate and indicates the canvas could not allocate a 2D context at all - typically resource exhaustion (too many live canvases/GPU memory) or a browser engine defect, since the worker already verified OffscreenCanvas exists.","triggerScenarios":"High-throughput worker OCR pipelines that allocate a new OffscreenCanvas per image without releasing prior ones; devices under severe memory pressure; browsers that disable 2D contexts in certain worker configurations.","commonSituations":"Batch-processing hundreds of images through the worker without throttling; low-memory mobile devices; automation environments with software rendering.","solutions":["Throttle concurrent OCR jobs in the worker so only a few OffscreenCanvas instances are live at once","Reuse a single OffscreenCanvas resized per job instead of allocating per image","Reduce input image resolution before OCR to lower canvas memory","If persistent, fall back to main-thread OCR"],"exampleFix":"// before\n// worker processes all jobs in parallel, each allocating OffscreenCanvas\nawait Promise.all(images.map(i => client.recognize(i))); // may exhaust contexts\n\n// after\nconst CONCURRENCY = 2;\nfor (let i = 0; i < images.length; i += CONCURRENCY) {\n  await Promise.all(images.slice(i, i + CONCURRENCY).map(img => client.recognize(img)));\n}","handlingStrategy":"fallback","validationCode":"// Hard to pre-validate; bound concurrent worker jobs instead\nconst MAX_CONCURRENT = 2; // keep live OffscreenCanvas count low","typeGuard":null,"tryCatchPattern":"try {\n  await workerClient.recognize(bitmap);\n} catch (e) {\n  if (e instanceof Error && e.message.includes(\"OCR worker\")) {\n    await workerClient.dispose();\n    return mainThreadOcr.recognize(bitmap); // fallback path\n  }\n  throw e;\n}","preventionTips":["Throttle concurrent recognize() calls through the worker","Feed downscaled images when device memory is low","Monitor worker restarts and degrade to main thread after repeated failures"],"tags":["worker","canvas","memory","offscreencanvas"],"backgroundTag":null,"analyzedSha":"2661c7c0ef5c613e8f93c6e93b2e052399f0f854","analyzedAt":"2026-08-14T20:17:30.180Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}