From 82c0472685ac65783e64605158d1a5327dfa9fe0 Mon Sep 17 00:00:00 2001 From: Florent Tapponnier Date: Fri, 17 Jul 2026 16:41:25 +0200 Subject: [PATCH] share-card: fix HTTP 500 on RPC benches (Satori AVIF/WebP + CSS var crashes) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two independent bugs causing pre-existing HTTP 500 on rpc-capabilities, polkadot-rpc, ws-head-latency-*, evm-block-builders share-card endpoints. Reproduced locally with bun dev + shimmed try/catch to unmask the mid-stream Satori errors. Bug 1: Satori (the JSX-to-SVG renderer next/og uses) only decodes PNG / JPEG / SVG. Feed it AVIF or WebP bytes and it throws 'TypeError: u2 is not iterable' mid-render, surfacing as opaque HTTP 500. publicnode.avif, drpc.webp, lava.webp all trigger this on the RPC benches. Fix: skip AVIF/WebP extensions in getProviderLogoDataUrl, fall back to the initials chip. Bug 2: chipBackground / chipTextColor from src/lib/brand.ts return CSS var references (e.g. 'var(--color-ink-soft)') when the provider has no registered brand color. Satori evaluates var(...) to the CSS-wide 'initial' keyword and rejects 'background: initial', crashing the whole PNG. evm-block-builders is a full-cohort case (Titan, BuilderNet, Quasar, Eureka, Builder+, Vanilla — none have brand colors or logos). Fix: resolve var(...) fallbacks to the concrete INK_SOFT / PAPER hex from the route's own palette. Both fixes scoped to share-card route.tsx. src/lib/brand.ts untouched (still used by the HTML surfaces where CSS variables resolve at browser render time). Verified locally: 40/40 renders (8 slugs x 5 templates) return HTTP 200 with valid 1200x630 PNGs. --- .../benchmarks/[slug]/share-card/route.tsx | 34 +++++++++++++++++-- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/src/app/benchmarks/[slug]/share-card/route.tsx b/src/app/benchmarks/[slug]/share-card/route.tsx index f4c49830..719725ec 100644 --- a/src/app/benchmarks/[slug]/share-card/route.tsx +++ b/src/app/benchmarks/[slug]/share-card/route.tsx @@ -145,6 +145,17 @@ const MIME: Record = { }; const _providerLogoCache = new Map(); + +// Satori (the JSX-to-SVG renderer next/og uses) only decodes PNG, JPEG +// and SVG. Feed it AVIF or WebP bytes and the internal decoder throws +// "TypeError: u2 is not iterable" mid-stream, which surfaces as a +// generic HTTP 500 on the share-card endpoint with no meaningful log. +// Skipping unsupported formats here falls back to the initials chip in +// CardProviderLogo, same as when the logo file is missing entirely. +// Root cause of the pre-existing 500 on rpc-capabilities (publicnode +// .avif, drpc / lava .webp), polkadot-rpc, ws-head-latency-*. +const SATORI_UNSUPPORTED_EXT = new Set([".avif", ".webp"]); + /** Returns a data URL for the registered logo file, or null if missing. * Result is cached per slug to avoid hitting the filesystem on every * render of the share-card. */ @@ -155,9 +166,14 @@ function getProviderLogoDataUrl(slug: string): string | null { _providerLogoCache.set(slug, null); return null; } + const ext = extname(rel).toLowerCase(); + if (SATORI_UNSUPPORTED_EXT.has(ext)) { + _providerLogoCache.set(slug, null); + return null; + } try { const buf = readFileSync(join(process.cwd(), "public", rel)); - const mime = MIME[extname(rel).toLowerCase()] ?? "image/png"; + const mime = MIME[ext] ?? "image/png"; const url = `data:${mime};base64,${buf.toString("base64")}`; _providerLogoCache.set(slug, url); return url; @@ -203,6 +219,18 @@ function CardProviderLogo({ ); } + // Resolve CSS `var(--color-*)` fallbacks from chipBackground / + // chipTextColor to the concrete palette hex. Satori evaluates + // `var(...)` to the CSS-wide `initial` keyword and rejects a + // `background: initial` declaration, taking the whole render down + // with a "Failed to parse declaration" error. Full-cohort case: + // evm-block-builders (Titan, BuilderNet, Quasar, Eureka, Builder+, + // Vanilla) — none have a registered logo or brand chip color so + // every row falls through here and the whole PNG fails to render. + const bgRaw = chipBackground(slug); + const fgRaw = chipTextColor(slug); + const bg = bgRaw.startsWith("var(") ? INK_SOFT : bgRaw; + const fg = fgRaw.startsWith("var(") ? PAPER : fgRaw; return (