Debugging von Next.js Server Actions: Bereitstellungsfehler, CORS und Payload-Limits
Ein praktischer Fehlerbehebungsführer, der erklärt, warum Next.js Server Actions und API-Routen stumm versagen oder rätselhafte Fehler auslösen, sowie konkrete Lösungen für jeden Fall.
Quellen: Common Server Action and API Route Errors in Next.js (and How to Fix Them)
Server Actions beseitigen einen großen Teil der Komplexität in der Backend-Logik von Next.js – man benötigt nicht länger eine eigene API-Route, um nur eine Formulareingabe zu verarbeiten. Doch diese Bequemlichkeit bringt ihr eigenes Set an Fehlermöglichkeiten mit sich, die völlig anders aussehen als die Bugs, auf die man im alten Pages Router stoßen würde. Hier ist eine Übersicht darüber, was häufig schiefgeht und wie man damit umgehen kann.
1. „Fehler: Server Action konnte nicht gefunden werden“ nach einem Deployment
Alles funktioniert einwandfrei auf Ihrem Rechner, bricht aber im Moment des Veröffentlichens einer neuen Version zusammen – Nutzer in laufenden Sitzungen erhalten plötzlich diesen Fehler.
Ursache: Server Actions werden mit IDs kompiliert, die an eine bestimmte Build-Version gebunden sind. Wenn jemand während des Deployings einen Browser-Tab geöffnet hat, referenziert sein Client weiterhin Action-IDs aus der alten Build-Version, die vom Server nicht mehr erkannt werden.
Lösung: Man kann dies bei Benutzern, die bereits während eines Deploys aktiv sind, nicht vollständig verhindern, aber man kann den Fehler besser handhaben, anstatt dass es stumm abstürzt:
'use client';
import { useTransition } from 'react';
function SubmitButton({ action }) {
const [isPending, startTransition] = useTransition(); async function handleClick() {
try {
await action();
} catch (err) {
if (err.message?.includes('Failed to find Server Action')) {
window.location.reload(); // force a fresh client with current build IDs
}
}
} return <button onClick={() => startTransition(handleClick)}>Submit</button>;
}
Das Erfassen dieses spezifischen Fehlers und die Aufforderung an den Benutzer, die Seite neu zu laden, bieten eine deutlich bessere Benutzererfahrung als ein roher, unerklärlicher Absturz.
2. Serveraktion läuft stumm nicht ab
Man ruft die Aktion auf, es passiert nichts, und es wird nirgends ein Fehler angezeigt.
Häufige Ursache: Fehlende 'use server'-Direktive am Anfang der Funktion oder die Schreibung der Aktion innerhalb eines Client-Komponenten ohne ordnungsgemäße Kennzeichnung.
// Wrong — no directive
async function saveUser(formData) {
await db.user.update(/* ... */);
}
Lösung:
'use server';
export async function saveUser(formData) {
await db.user.update(/* ... */);
}
Falls die Aktion im selben Datei wie ein Client Component definiert ist, muss die Direktive an der Spitze der selben Funktion stehen und nicht nur am Anfang der Datei:
'use client';
export default function Form() {
async function saveUser(formData) {
'use server'; // required here specifically
await db.user.update(/* ... */);
}
return <form action={saveUser}>{/* ... */}</form>;
}
3. „Fehler: Nur einfache Objekte können an Client Components übergeben werden“
Dies tritt auf, wenn der Rückgabewert einer Server Action nicht serialisierbar ist – beispielsweise eine Klasseninstanz, ein Date-Objekt in einer ungewöhnlichen Struktur oder ein Datenbankmodell mit eigenen Methoden.
'use server';
export async function getUser(id) {
return await db.user.findUnique({ where: { id } }); // ORM might return a class instance
}
Lösung – das Ergebnis manuell in ein einfaches Objekt umformen:
'use server';
export async function getUser(id) {
const user = await db.user.findUnique({ where: { id } });
return {
id: user.id,
name: user.name,
email: user.email,
createdAt: user.createdAt.toISOString(), // Dates need explicit serialization too
};
}
Geben Sie kein direktes ORM-Ergebnis an ein Client Component weiter. Konvertieren Sie es zunächst in ein einfaches, serialisierbares Objekt.
4. Server Action funktioniert, aber die Formulardaten kommen als undefined zurück
<form action={createPost}>
<input name="title" />
</form>
'use server';
export async function createPost(formData) {
console.log(formData.title); // undefined
}
Ursache: Der Argument formData, das Sie erhalten, ist eine native FormData-Instanz und kein gewöhnliches JavaScript-Objekt, weshalb Sie .get() aufrufen müssen, um Werte daraus abzurufen.
Lösung:
'use server';
export async function createPost(formData) {
const title = formData.get('title');
console.log(title); // correct
}
5. „Body exceeded 1mb limit“ bei Server Actions
Server Actions legen eine Standardbegrenzung für die Größe des Anfragekörpers fest, was zum Problem wird, sobald es um Dateiuploads oder große Formulardatenmengen geht.
Lösung – erhöhen Sie die Grenze explizit in next.config.js:
// next.config.js
module.exports = {
experimental: {
serverActions: {
bodySizeLimit: '10mb',
},
},
};
Für echte Dateiuploads ist es in der Regel besser, die Weiterleitung der Datei über eine Server Action ganz zu umgehen. Laden Sie die Datei direkt vom Client in ein Objektspeichersystem wie S3, R2 oder ähnliches mit einer signierten URL hoch. Dadurch wird die Größenbegrenzung umgangen und der Server bleibt entlastet.
6. CORS-Fehler bei API-Endpunkten, die von einer anderen Herkunft aus aufgerufen werden
Dieser Fehler tritt in der Regel auf, wenn eine Mobile-App, ein Browser-Erweiterung oder eine separate Frontend-Anwendung direkt auf Ihre Next.js-API-Endpunkte zugreift, anstatt über Ihre eigenen Seiten.
Access to fetch at 'https://yourapp.com/api/data' from origin 'https://otherapp.com' has been blocked by CORS policy
Lösung – setzen Sie die Header explizit innerhalb des Route-Handlers fest:
// app/api/data/route.js
export async function GET(request) {
const data = await getData();
return Response.json(data, {
headers: {
'Access-Control-Allow-Origin': 'https://otherapp.com', // never '*' for authenticated routes
'Access-Control-Allow-Methods': 'GET, POST',
},
});
}
export async function OPTIONS() {
return new Response(null, {
headers: {
'Access-Control-Allow-Origin': 'https://otherapp.com',
'Access-Control-Allow-Methods': 'GET, POST',
},
});
}
Vermeiden Sie es, Access-Control-Allow-Origin: '*' auf Endpunkten zu setzen, die auf Authentifizierungs-Cookies angewiesen sind. Dadurch werden Benutzerdaten faktisch an jede Herkunft übergeben, die danach fragt.
7. Der Route-Handler gibt bei Fehlern HTML anstelle von JSON zurück
Wenn ein Route-Handler eine unverarbeitete Ausnahme auslöst, erhält der Client kein sauberes JSON-Fehlerobjekt zurück – stattdessen wird die eingebaute HTML-Fehlerseite von Next.js angezeigt. Das ist ein Problem für jeden Client-Code, der davon ausgeht, dass er sicherlich response.json() aufrufen kann.
Lösung – umschließen Sie die Handler-Logik in einen try/catch-Block, der stets JSON zurücksendet:
export async function POST(request) {
try {
const body = await request.json();
const result = await processOrder(body);
return Response.json(result);
} catch (error) {
return Response.json(
{ error: error.message ?? 'Something went wrong' },
{ status: 500 }
);
}
}
Dadurch erhält der Client stets Inhalte, die als JSON interpretiert werden können, unabhängig davon, ob der Anfrage Erfolg oder Misserfolg beschieden war – anstelle eines HTML-Dokuments, das die spätere .json()-Aufrufung zum Scheitern bringt.
Warum diese Fehler schlimmer wirken als Fehler bei herkömmlichen API-Routen
Serveraktionen fassen die Client- und Serverlogik in derselben Datei zusammen, wodurch es überraschend einfach ist, eine Grenze unbemerkt zu überschreiten. Ein Rückgabewert kann in Ihrem Editor völlig in Ordnung erscheinen und dennoch zur Laufzeit fehlschlagen, weil er nicht serialisierbar ist, oder eine fehlende Direktive kann stillschweigend versagen anstatt dort einen Fehler auszulösen, wo man es erwartet. Die üblichen Debugging-Methoden aus REST-basierten APIs – die Netzwerkkarte öffnen, die Antwort überprüfen – helfen hier nicht immer, denn mehrere dieser Fehler treten bereits auf, bevor überhaupt eine Netzwerkanfrage gesendet wird.
Für Teams, die Dashboards mit vielen Formularen, Dateiuploads und Änderungen auf verschiedenen Seiten erstellen, lohnt es sich, diese Muster – Serialisierung, Fehlerbehandlung sowie Limits für die Größe des Anfragekörpers – einmal einheitlich im gesamten Codebase zu lösen, anstatt immer wieder die gleichen Probleme bei jedem neuen Formular zu beheben. Einige vorgefertigte Admin-Vorlagen für SaaS-Produkte mit KI-Technologie liefern bereits Server Actions sowie korrekt konfigurierte API-Pfade, einschließlich Validierung mittels Zod und einer von Anfang an integrierten Fehlerbehandlung, sodass diese sieben Probleme nur einmal gelöst werden müssen und nicht bei jedem neu erstellten Formular wieder auftauchen.
Fazit
Server Actions reduzieren einen Großteil der Standardstruktur, die API-Routen früher erforderten, bringen aber auch ihre eigenen Fehlermöglichkeiten mit sich: Build-IDs, die nach einem Deployment veralten, fehlende Anweisungen, Rückgabewerte, die nicht serialisiert werden können, Fehlgebrauch von FormData, Obergrenzen für die Größe des Anhangs, CORS-Fehler zwischen verschiedenen Ursprungsdomänen sowie Fehlerantworten, die eigentlich kein JSON sind. Alle sieben Probleme treten häufig auf, können alle in nur wenigen Zeilen behoben werden und erfordern alle eine schnelle Überprüfung, bevor man annimmt, etwas Komplexeres sei schiefgelaufen.