Error: Unexpected Server Error in loader
What causes this
A Remix loader function threw an unhandled error or returned an invalid response. Loaders run on the server before rendering a route, and if they fail, Remix shows this generic error instead of your page.
Common causes:
- An API call or database query failed inside the loader
- The loader doesnβt return a proper
Responseorjson()result - Environment variables are missing on the server
- A thrown
Responsehas an unexpected status code - TypeScript type errors that only surface at runtime
- The loader accidentally returns
undefined
Fix 1: Always return a valid response
// β Returns undefined if the condition is false
export async function loader({ params }: LoaderFunctionArgs) {
if (params.id) {
return json({ item: await getItem(params.id) });
}
// falls through β returns undefined
}
// β
Always return something
export async function loader({ params }: LoaderFunctionArgs) {
if (!params.id) {
throw new Response('Not Found', { status: 404 });
}
return json({ item: await getItem(params.id) });
}
Fix 2: Wrap in try/catch
export async function loader({ request }: LoaderFunctionArgs) {
try {
const data = await fetchFromAPI(request);
return json(data);
} catch (error) {
console.error('Loader failed:', error);
throw new Response('Failed to load data', { status: 500 });
}
}
Throwing a Response lets Remix render your error boundary instead of the generic error page.
Fix 3: Check environment variables
export async function loader() {
const apiKey = process.env.API_KEY;
if (!apiKey) {
throw new Error('API_KEY is not set'); // This causes the generic error
}
// β
Better: throw a Response so the error boundary catches it
if (!apiKey) {
throw new Response('Server configuration error', { status: 500 });
}
const data = await fetch(`https://api.example.com?key=${apiKey}`);
return json(await data.json());
}
Make sure your .env file is loaded and your deployment platform has the variables set.
Fix 4: Add an ErrorBoundary
Give users a better experience when loaders fail:
export function ErrorBoundary() {
const error = useRouteError();
if (isRouteErrorResponse(error)) {
return <div>Error {error.status}: {error.statusText}</div>;
}
return <div>Something went wrong. Please try again.</div>;
}
Fix 5: Check the server logs
The generic error page hides the real error. Check your terminal or deployment logs:
# Local dev β check the terminal running `remix dev`
# Vercel β check function logs
vercel logs --follow
# Fly.io
fly logs
The actual error message in the logs tells you exactly what went wrong.
Related resources
How to prevent it
- Always wrap external calls (APIs, databases) in try/catch inside loaders
- Throw
Responseobjects instead ofErrorobjects β Remix handles them better - Add
ErrorBoundaryexports to every route that has a loader - Validate environment variables at startup, not inside individual loaders
- Use
invariant()fromtiny-invariantfor runtime assertions
Related: React Interview Questions Β· Tanstack Router Not Found Fix