i18n-Catalogues nicht im standalone-Bundle (P0)

Datum

14.05.2026

Dauer

15 h 24 min

Severity
P0 · Kritisch
Status

Behoben

Betroffene Services

  • /chat
  • /dashboard
  • /vault
  • /sprint
  • /whiteboard
  • alle (app)/* Routen

Summary

next-intl dynamic-imports wurden von Next.js `output: 'standalone'` nicht getraced. Plus die getRequestConfig-Wiring fehlte komplett. Alle eingeloggten User sahen einen friendly Error-Boundary statt der App. Health-Endpoint blieb grün, weil der das (app)-Layout bypassed.

Root Cause

Der i18n-Foundation-Commit shipte einen `getMessages()`-Helper, der die deutsche und englische JSON-Katalogdatei via `await import('../../messages/de.json')` dynamisch importierte. Next.js `output: 'standalone'` folgt beim File-Tracing ausschließlich statischen Import-Graphen — dynamische Imports auf relative JSON-Pfade landen deshalb nicht im Bundle. Zusätzlich fehlte der `createNextIntlPlugin`-Wrapper in `next.config.ts`, sodass `getRequestConfig` zur Laufzeit nie aufgerufen wurde. Beim ersten Request auf eine `(app)/*`-Route schmiss der Server-Render `MODULE_NOT_FOUND`, weshalb React Server Components den global-error-Boundary statt der App rieten. Der `/api/health`-Endpunkt blieb grün, weil sämtliche Health-Probes pure API-Routen sind und das `(app)/layout.tsx` nie laden.

Resolution

Hotfix #654 hat die JSON-Katalogdateien auf statische Imports umgestellt und gleichzeitig `outputFileTracingIncludes` als Belt-and-Suspenders für `messages/**/*.json` in `next.config.ts` ergänzt — damit lebt der Katalog garantiert im standalone-Bundle, selbst wenn ein späterer Refactor versehentlich wieder zu Dynamic-Imports zurückspringt. Direkt im Anschluss landete #656 mit dem fehlenden `createNextIntlPlugin`-Wrapper plus expliziter `i18n/request.ts`-Konfiguration, sodass `getRequestConfig` beim Boot tatsächlich gewired ist. Wir haben einen Regressions-Test in `src/i18n/__tests__/server.test.ts` ergänzt, der die Reference-Equality und die Fallback-Strategie pinned. Live-Verify lief um 17:02 UTC über Chrome MCP auf `/chat` und `/dashboard`; die finale Confirmation kam um 17:58 UTC nach dem Background-Reload aller eingeloggten Sessions.

Preventive Actions

  • Marketing-Routes plus eine `(app)/`-Sample-Route als Smoke-Test in den /api/health-Endpoint aufnehmen.

  • outputFileTracingIncludes als Belt-and-Suspenders für alle runtime-relevanten JSON- und YAML-Configs in `next.config.ts` pflegen.

  • Next.js standalone-Build im CI-Smoke-Test booten (`node .next/standalone/server.js`) und gegen `/chat` + `/dashboard` curl-en, bevor ein Merge in main erlaubt ist.

  • Migration zu next-intl in einen Phase-2-Sprint aufteilen statt in einem Single-PR — getrennte PRs für Plugin-Wrapper, Catalog-Migration und Locale-Switcher.

  • Sentry.captureException in `app/global-error.tsx` wiren, damit layout-level RSC-Throws pagen statt nur warning-level-loggen.

Timeline

Zeit (UTC)Ereignis
01:46Bug-Commit (i18n-Foundation) wird nach main gemerged und auto-deployed.
16:30Erster User-Report: `/chat` zeigt friendly Error-Boundary statt der App.
16:38cc-samy reproduziert über Chrome MCP, isoliert Standalone-Tracing-Miss.
16:45Hotfix-PR #654 (static-imports + outputFileTracingIncludes) gemerged.
16:58Follow-up-PR #656 (createNextIntlPlugin-Wrapper) gemerged.
17:02Live-Verify über Chrome MCP: `/chat` und `/dashboard` rendern wieder.
17:58Finale Confirmation nach Background-Reload aller eingeloggten Sessions.