Stirling-Tools/Stirling-PDF · warning · Error

Failed to get canvas context

Error message

Failed to get canvas context

What it means

Thrown by downloadAndOptimizeAvatar() when canvas.getContext('2d') returns null. The 2D rendering context can be unavailable in headless or GPU-constrained browser environments, very old browsers, or when too many canvas contexts are simultaneously allocated. The caller syncOAuthAvatar() catches this and degrades gracefully to the existing avatar.

Source

Thrown at frontend/editor/src/saas/services/avatarSyncService.ts:90

    if (!response.ok) {
      throw new Error(
        `Failed to download avatar: ${response.status} ${response.statusText}`,
      );
    }

    const blob = await response.blob();

    // 2. Create image bitmap
    const img = await createImageBitmap(blob);

    // 3. Create canvas and draw scaled image
    const canvas = document.createElement("canvas");
    canvas.width = AVATAR_SIZE;
    canvas.height = AVATAR_SIZE;
    const ctx = canvas.getContext("2d");

    if (!ctx) {
      throw new Error("Failed to get canvas context");
    }

    // Draw image scaled to fit (maintains aspect ratio, centered)
    const scale = Math.min(AVATAR_SIZE / img.width, AVATAR_SIZE / img.height);
    const x = (AVATAR_SIZE - img.width * scale) / 2;
    const y = (AVATAR_SIZE - img.height * scale) / 2;
    ctx.drawImage(img, x, y, img.width * scale, img.height * scale);

    // 4. Convert to PNG blob with quality optimization
    return new Promise((resolve, reject) => {
      canvas.toBlob(
        (optimizedBlob) => {
          if (!optimizedBlob) {
            reject(new Error("Failed to create optimized blob"));
            return;
          }

          // Check file size

View on GitHub (pinned to 9ef20dcab8)

Solutions

  1. Detect canvas 2D support before attempting avatar optimization and skip to raw upload
  2. Fall back to uploading the unresized blob if canvas is unavailable
  3. Reduce concurrent canvas operations if the context limit is the cause
  4. Test in a non-headless browser to confirm the issue is environment-specific

Example fix

// before
const ctx = canvas.getContext("2d");
if (!ctx) {
  throw new Error("Failed to get canvas context");
}

// after
const ctx = canvas.getContext("2d");
if (!ctx) {
  console.warn("[Avatar Sync] Canvas 2D unavailable, uploading raw blob");
  return blob; // skip resize, upload original
}
Defensive patterns

Strategy: fallback

Validate before calling

// Detect canvas 2D support before attempting optimization
function hasCanvas2D(): boolean {
  try {
    const c = document.createElement('canvas');
    return c.getContext('2d') !== null;
  } catch {
    return false;
  }
}
if (!hasCanvas2D()) {
  return rawBlob; // skip resize
}

Try / catch

// syncOAuthAvatar already catches all errors and returns false:
// downloadAndOptimizeAvatar throws, but the outer syncOAuthAvatar swallows it.
// No additional catch needed if calling through syncOAuthAvatar.

Prevention

When it happens

Trigger: Browser running in headless mode without GPU acceleration; browser security/privacy settings disabling canvas (some anti-fingerprinting extensions); extremely memory-constrained environment; old browser without full Canvas 2D support.

Common situations: Headless browser testing (CI/CD, Puppeteer/Playwright) without GPU; user has an anti-fingerprinting extension that blocks canvas context creation; browser under extreme memory pressure.

Related errors


AI-assisted analysis of Stirling-Tools/Stirling-PDF@9ef20dcab8 (2026-08-13). Data as JSON: /api/errors/b2c172a340fd5664. Report an issue: GitHub.