linshenkx/prompt-optimizer · error · Error

Canvas is not available

Error message

Canvas is not available

What it means

Thrown by ensureCanvas in packages/ui/src/utils/favorite-share-export.ts when neither the provided canvasFactory nor document.createElement('canvas') yields an object with a getContext method. This is an environment-capability guard: in normal browsers a canvas always has getContext, so hitting it means canvas support is absent or the factory returned something invalid. It fires before any 2D rendering begins.

Source

Thrown at packages/ui/src/utils/favorite-share-export.ts:932

        } else {
          fail();
        }
      });
    }());
  </script>
</body>
</html>`

  return {
    blob: new Blob([html], { type: 'text/html;charset=utf-8' }),
    result,
  }
}

const ensureCanvas = (canvasFactory?: () => HTMLCanvasElement): HTMLCanvasElement => {
  const canvas = canvasFactory?.() || document.createElement('canvas')
  if (!canvas.getContext) {
    throw new Error('Canvas is not available')
  }
  return canvas
}

const wrapCanvasText = (
  context: CanvasRenderingContext2D,
  text: string,
  maxWidth: number,
): string[] => {
  const lines: string[] = []
  for (const rawLine of text.split(/\r?\n/)) {
    const words = rawLine.split(/(\s+)/).filter(Boolean)
    let line = ''
    for (const word of words) {
      if (context.measureText(word).width > maxWidth) {
        for (const char of Array.from(word)) {
          const next = `${line}${char}`
          if (line && context.measureText(next).width > maxWidth) {

View on GitHub (pinned to 3e677b1d9f)

Solutions

  1. If in tests, pass a canvasFactory returning a real or adequately mocked canvas (with getContext and a 2D context stub)
  2. Guard the export call: only invoke PNG share export when typeof document !== 'undefined' and document.createElement('canvas').getContext exists
  3. In SSR, run the export only after mount in the browser, or use a node-canvas-backed factory
  4. Fix a canvasFactory that returns a non-canvas object (e.g. a context instead of the canvas)

Example fix

// before
const canvas = ensureCanvas(() => fakeCanvasStub) // stub lacks getContext -> throws Canvas is not available

// after
const canvas = ensureCanvas(() => realCanvas) // or omit factory in a real browser DOM
const context = canvas.getContext('2d')
if (!context) throw new Error('Canvas 2D context is not available')
Defensive patterns

Strategy: validation

Validate before calling

const canUseCanvas = (): boolean =>
  typeof document !== 'undefined' &&
  typeof document.createElement === 'function' &&
  typeof document.createElement('canvas').getContext === 'function'

if (!canUseCanvas()) {
  // hide PNG share button or fall back to text sharing
}

Type guard

const isCanvasLike = (el: unknown): el is HTMLCanvasElement =>
  typeof el === 'object' && el !== null && typeof (el as HTMLCanvasElement).getContext === 'function'

Try / catch

try {
  const blob = await exportFavoriteSharePng(options)
} catch (e) {
  if (e instanceof Error && e.message === 'Canvas is not available') {
    // fall back to text-only share, or disable the feature in this environment
    return
  }
  throw e
}

Prevention

When it happens

Trigger: Running the PNG share-image export in an environment without DOM/canvas (SSR, Node, jsdom, Web Workers without DOM access); passing a canvasFactory that returns null, undefined, a plain object, or a detached mock lacking getContext; or an environment where HTMLCanvasElement is not implemented.

Common situations: Unit tests in jsdom that trigger image export; server-side rendering paths that accidentally call share-image generation; mocking document.createElement without providing getContext; embedded webviews with canvas disabled. Note the factory result is used as-is, so a falsy factory result silently falls back to document.createElement — the error only fires when both paths fail.


AI-assisted analysis of linshenkx/prompt-optimizer@3e677b1d9f (2026-08-27). Data as JSON: /api/errors/903cbb432a989893. Report an issue: GitHub.