{"record":{"id":"267ac3c6823cd4e0","repo":"DavidHDev/react-bits","slug":"could-not-get-2d-context","errorCode":null,"errorMessage":"Could not get 2d context","messagePattern":"Could not get 2d context","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/ts-tailwind/Components/CircularGallery/CircularGallery.tsx","lineNumber":135,"sourceCode":"    console.error('CircularGallery: unable to load font from', fontUrl, error);\n    return font;\n  }\n}\n\nfunction getFontSize(font: string): number {\n  const match = font.match(/(\\d+)px/);\n  return match ? parseInt(match[1], 10) : 30;\n}\n\nfunction createTextTexture(\n  gl: GL,\n  text: string,\n  font: string = 'bold 30px monospace',\n  color: string = 'black'\n): { texture: Texture; width: number; height: number } {\n  const canvas = document.createElement('canvas');\n  const context = canvas.getContext('2d');\n  if (!context) throw new Error('Could not get 2d context');\n\n  context.font = font;\n  const metrics = context.measureText(text);\n  const textWidth = Math.ceil(metrics.width);\n  const fontSize = getFontSize(font);\n  const textHeight = Math.ceil(fontSize * 1.2);\n\n  canvas.width = textWidth + 20;\n  canvas.height = textHeight + 20;\n\n  context.font = font;\n  context.fillStyle = color;\n  context.textBaseline = 'middle';\n  context.textAlign = 'center';\n  context.clearRect(0, 0, canvas.width, canvas.height);\n  context.fillText(text, canvas.width / 2, canvas.height / 2);\n\n  const texture = new Texture(gl, { generateMipmaps: false });","sourceCodeStart":117,"sourceCodeEnd":153,"githubUrl":"https://github.com/DavidHDev/react-bits/blob/c7109dccb42e06592d1d9bc50bc87204697240e2/src/ts-tailwind/Components/CircularGallery/CircularGallery.tsx#L117-L153","documentation":"createTextTexture allocates an offscreen canvas to rasterize a disc title into a GL texture via a 2D context. getContext('2d') returns null only when a 2D context cannot be created (context cap, memory pressure, or non-browser env), so it throws before measureText/fillText can run.","triggerScenarios":"document.createElement('canvas').getContext('2d') returns null at src/ts-tailwind/Components/CircularGallery/CircularGallery.tsx:135 inside createTextTexture, which is called per gallery item to build label textures.","commonSituations":"A page with many canvases/WebGL contexts hitting the browser context cap; SSR/jsdom where 2D canvas is unavailable; heavy memory pressure; the canvas element was already bound to an incompatible context type; rapid creation of text textures without cleanup.","solutions":["Reduce total live canvas/WebGL contexts on the page; pool and reuse a single offscreen 2D canvas for all labels.","Guard getContext('2d') and return a placeholder/empty texture when unavailable.","Pre-render labels server-side or cache textures instead of allocating per item.","Ensure the code executes in a real browser with canvas 2D support."],"exampleFix":"// before\nconst context = canvas.getContext('2d');\nif (!context) throw new Error('Could not get 2d context');\n\n// after (shared, guarded)\nconst context = shared2DCanvas.getContext('2d');\nif (!context) return { texture: emptyTexture(gl), width: 1, height: 1 };","handlingStrategy":"validation","validationCode":"function canGet2D(): boolean {\n  try { return !!document.createElement('canvas').getContext('2d'); }\n  catch { return false; }\n}","typeGuard":"function get2D(canvas: HTMLCanvasElement): CanvasRenderingContext2D | null {\n  return canvas.getContext('2d');\n}","tryCatchPattern":"const context = canvas.getContext('2d');\nif (!context) {\n  return { texture: emptyTexture(gl), width: 1, height: 1 };\n}","preventionTips":["Pool and reuse a single offscreen 2D canvas for all label textures.","Guard getContext('2d') and return a placeholder texture on failure.","Limit the number of live canvases/WebGL contexts on the page.","Pre-render or cache label textures instead of allocating per item."],"tags":["canvas2d","context","resource-limits","texture","circular-gallery"],"backgroundTag":null,"analyzedSha":"c7109dccb42e06592d1d9bc50bc87204697240e2","analyzedAt":"2026-08-13T02:32:07.327Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}