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.tsxrenders DiceBear avatars withunoptimized.lumie-frontend/next.config.tsno longer enablesdangerouslyAllowSVG.- 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
unoptimizedpattern asPersonCardGrid. - Do not reintroduce
images.dangerouslyAllowSVGfor DiceBear unless the rendering path changes away from the in-process optimizer. - Verify the current guardrails in source:
lumie-frontend/src/shared/ui/PersonCardGrid.tsxincludesunoptimizedon the DiceBear avatar image.lumie-frontend/next.config.tscontains 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:
| Path | Behavior | Memory risk |
|---|---|---|
| Browser renders DiceBear SVG directly | SVG is fetched and painted by the browser. | Low for the Next.js server. |
next/image optimizes DiceBear SVG | Next.js fetches, rasterizes, and re-encodes each avatar. | High when list pages render many avatars. |
| Static WebP media | Served 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
- Confirm the pod termination reason is
OOMKilledand capture the memory limit. - Check whether the failing page renders many DiceBear URLs through
next/image. - Verify
dangerouslyAllowSVGis not enabled innext.config.ts. - Add
unoptimizedto DiceBear avatarImagecall sites or replace them with a shared avatar component that already does so. - 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/imagerequests 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.