Skip to main content

Next Image DiceBear SVG OOM

This troubleshooting note covers the May 2026 regression introduced by 3be71e0 and fixed by 37adf6e.

Symptoms

  • Student or staff list pages crash the frontend pod after rendering many avatar cards.
  • Pod memory climbs close to the 256 MiB limit and the container is OOMKilled.
  • The failures cluster around pages that render DiceBear avatars through next/image.

Diagnosis

Check the current avatar call site in lumie-frontend/src/shared/ui/PersonCardGrid.tsx. DiceBear avatars are now rendered with unoptimized:

<Image
src={getAvatarUrl(item.avatarSeed, 80)}
width={40}
height={40}
unoptimized
/>

That matches the warning left in lumie-frontend/next.config.ts: DiceBear SVGs must not go through the Sharp-based optimizer.

Root Cause

Commit 3be71e0 enabled images.dangerouslyAllowSVG in next.config.ts so DiceBear SVG avatars could be optimized. That removed Next.js's default SVG block, but it also sent each avatar through the optimizer pipeline.

On card-grid pages, PersonCardGrid rendered many avatars at once. Once SVG optimization was allowed, Next.js rasterized and re-encoded each SVG in-process. Commit 37adf6e documents the observed result: pod RSS spiked to about 254 MiB against a 256 MiB limit.

0652e18 later reduced general image optimizer load by removing AVIF and capping image sizes, but that was a follow-up optimization, not the DiceBear root cause.

Fix

The durable fix is the combination that exists today:

  • lumie-frontend/src/shared/ui/PersonCardGrid.tsx renders DiceBear avatars with unoptimized.
  • lumie-frontend/next.config.ts no longer enables dangerouslyAllowSVG.
  • Static landing media is handled separately through pre-optimized WebP assets instead of relying on heavier runtime transforms.

Prevention And Verification

  • Treat remote DiceBear avatars as already-optimized SVG assets. New avatar components should follow the same unoptimized pattern as PersonCardGrid.
  • Do not reintroduce images.dangerouslyAllowSVG for DiceBear unless the rendering path changes away from the in-process optimizer.
  • Verify the current guardrails in source:
    • lumie-frontend/src/shared/ui/PersonCardGrid.tsx includes unoptimized on the DiceBear avatar image.
    • lumie-frontend/next.config.ts contains the comment explaining why SVG optimization stays disabled.

Operational Detail

This was a memory-pressure failure, not an avatar availability failure. The DiceBear endpoint continued to return SVGs, but the frontend pod became the expensive conversion point after SVG optimization was enabled.

The important distinction is:

PathBehaviorMemory risk
Browser renders DiceBear SVG directlySVG is fetched and painted by the browser.Low for the Next.js server.
next/image optimizes DiceBear SVGNext.js fetches, rasterizes, and re-encodes each avatar.High when list pages render many avatars.
Static WebP mediaServed as already prepared assets.Bounded and predictable.

When a page crashes only after a grid or roster grows, inspect the number of unique image optimizer requests before assuming the React component tree itself is leaking memory.

Recovery Playbook

  1. Confirm the pod termination reason is OOMKilled and capture the memory limit.
  2. Check whether the failing page renders many DiceBear URLs through next/image.
  3. Verify dangerouslyAllowSVG is not enabled in next.config.ts.
  4. Add unoptimized to DiceBear avatar Image call sites or replace them with a shared avatar component that already does so.
  5. Redeploy and reproduce the largest realistic roster or staff grid.

Verification Evidence

Before closing the issue, record:

  • the previous and current pod memory ceiling,
  • the largest roster or grid size used for reproduction,
  • whether /_next/image requests are no longer generated for DiceBear SVG URLs,
  • whether pod RSS remains below the memory limit after repeated navigation,
  • whether static image optimization settings still support non-SVG media.

Safe Future Changes

Reconsider SVG optimization only if one of these changes is true:

  • DiceBear avatars are pre-rendered to PNG or WebP outside the request path,
  • avatars are cached behind a service that returns bounded raster assets,
  • frontend pod memory limits and image optimizer concurrency are intentionally re-sized and load-tested.

Until then, unoptimized is part of the avatar contract, not a cosmetic option.