500 Internal Error
Error in load function
Your SvelteKit load function threw an unhandled error. SvelteKit catches it and shows a generic 500 page. The actual error is in your terminal/server logs โ the browser only shows the sanitized message.
How SvelteKit load functions work
SvelteKit has two types of load functions:
+page.server.ts(server load) โ Runs only on the server. Can access databases, env vars, secrets. Never sent to the browser.+page.ts(universal load) โ Runs on both server (SSR) and client (navigation). Canโt access server-only resources.
Both must return an object. If they throw, SvelteKit shows the error page. If they return nothing, the page gets undefined data.
Fix 1: Add error handling to fetch calls
The most common cause โ an API call fails and you donโt handle it.
// src/routes/+page.server.ts
import { error } from '@sveltejs/kit';
export async function load({ fetch }) {
// โ No error handling โ any failure crashes the page
const res = await fetch('/api/data');
const data = await res.json(); // Crashes if response isn't JSON
return { data };
}
// โ
Handle errors properly
export async function load({ fetch }) {
const res = await fetch('/api/data');
if (!res.ok) {
throw error(res.status, {
message: 'Failed to load data',
details: await res.text()
});
}
try {
return { data: await res.json() };
} catch (e) {
throw error(500, 'Invalid JSON response from API');
}
}
Fix 2: Check your terminal for the real error
The browser shows a generic โ500 Internal Errorโ for security (to avoid leaking stack traces). The actual error with full details is in your terminal where npm run dev is running.
# Terminal output shows the real error:
# TypeError: Cannot read properties of undefined (reading 'id')
# at load (src/routes/posts/[slug]/+page.server.ts:12:24)
In production: Check your hosting platformโs logs (Vercel Functions tab, Cloudflare Workers logs, or your serverโs stdout).
Fix 3: Handle null/undefined data
If your load function depends on URL params or database lookups:
// src/routes/posts/[slug]/+page.server.ts
import { error } from '@sveltejs/kit';
export async function load({ params }) {
const post = await db.post.findUnique({
where: { slug: params.slug }
});
// โ post might be null โ accessing .title crashes
return { title: post.title, content: post.content };
// โ
Handle the null case explicitly
if (!post) {
throw error(404, `Post "${params.slug}" not found`);
}
return { title: post.title, content: post.content };
}
Fix 4: Missing or wrong return value
Load functions must return a plain object. Forgetting to return, or returning the wrong type, causes errors.
// โ No return statement โ page.data is undefined
export async function load() {
const data = await fetchData();
// Forgot to return!
}
// โ Returning a non-object
export async function load() {
return "hello"; // Must be an object
}
// โ Returning a Promise without await
export async function load() {
return { data: fetchData() }; // data is a Promise, not the resolved value
}
// โ
Correct
export async function load() {
const data = await fetchData();
return { data };
}
Fix 5: Environment variables missing
Server-side load functions can access env vars, but they might not be set in all environments.
import { env } from '$env/dynamic/private';
import { error } from '@sveltejs/kit';
export async function load() {
if (!env.DATABASE_URL) {
throw error(500, 'DATABASE_URL not configured');
}
if (!env.API_KEY) {
throw error(500, 'API_KEY not configured');
}
const res = await fetch(env.API_URL, {
headers: { Authorization: `Bearer ${env.API_KEY}` }
});
return { data: await res.json() };
}
Common issue: Env vars work in npm run dev (from .env) but fail in production because they werenโt set in the hosting platformโs environment settings.
Fix 6: Server load vs universal load confusion
Using server-only features in a universal load function:
// โ +page.ts (universal) โ can't access server-only modules
import { db } from '$lib/server/database'; // Error!
export async function load() {
return { posts: await db.post.findMany() };
}
// โ
Use +page.server.ts for server-only code
// src/routes/+page.server.ts
import { db } from '$lib/server/database';
export async function load() {
return { posts: await db.post.findMany() };
}
Rule: If your load function needs databases, env vars, or secrets โ use +page.server.ts. If it only needs fetch โ use +page.ts (runs on both server and client, enabling client-side navigation without server round-trips).
Fix 7: Depends and invalidation issues
If you use depends() for invalidation, make sure the load function handles re-runs gracefully:
export async function load({ depends, fetch }) {
depends('app:posts'); // Register dependency
const res = await fetch('/api/posts');
if (!res.ok) {
// Don't throw on invalidation failures โ show stale data instead
console.error('Failed to refresh posts');
return { posts: [], error: 'Failed to load' };
}
return { posts: await res.json() };
}
Fix 8: Parent load function errors cascade
If a layoutโs load function fails, all child pages fail too:
// src/routes/+layout.server.ts
export async function load({ locals }) {
// If this throws, ALL pages under this layout get 500
const user = await getUser(locals.session);
return { user };
}
Fix: Make layout load functions resilient:
export async function load({ locals }) {
try {
const user = await getUser(locals.session);
return { user };
} catch {
return { user: null }; // Don't crash all pages
}
}
Add a proper error page
Create +error.svelte to show users a helpful error instead of the default:
<!-- src/routes/+error.svelte -->
<script>
import { page } from '$app/stores';
</script>
<div class="error-page">
<h1>{$page.status}</h1>
<p>{$page.error?.message || 'Something went wrong'}</p>
{#if $page.status === 404}
<p>The page you're looking for doesn't exist.</p>
{/if}
<a href="/">Go home</a>
</div>
You can have error pages at different levels โ src/routes/+error.svelte catches all errors, while src/routes/dashboard/+error.svelte only catches errors in the dashboard section.
Debugging tips
- Always check the terminal โ the real error is there, not in the browser
- Add
console.logat the start of your load function to confirm itโs being called - Use SvelteKitโs
handleErrorhook for centralized error logging:// src/hooks.server.ts export function handleError({ error, event }) { console.error('Unhandled error:', error); console.error('URL:', event.url.pathname); return { message: 'Internal error' }; } - Test with
?__data.jsonappended to the URL โ shows the raw load function output
FAQ
Why does my load function work in dev but fail in production?
Common causes: missing environment variables, different Node.js version, database not accessible from production server, or API URLs that differ between environments. Check your hosting platformโs function logs.
Can I use try/catch inside load functions?
Yes, and you should. Wrap external calls in try/catch. If you want to show an error page, re-throw using SvelteKitโs error() helper. If you want to show partial data, catch the error and return a fallback.
Whatโs the difference between throwing error(404) and returning { status: 404 }?
throw error(404, 'message') triggers the error page (+error.svelte). Returning an object with a status property just passes that data to the page component โ it doesnโt trigger the error page.
Why does my load function run twice?
During SSR, the server load runs first. Then on the client, the universal load runs again for hydration. If youโre seeing double API calls, move the fetch to +page.server.ts (server-only) โ the data is serialized and sent to the client without re-fetching.