HTTP 500 Internal Server Error — What It Means and How to Debug It
HTTP 500 means the server encountered an unexpected error it couldn't handle. Unlike 4xx errors, 500 is always the server's fault. Here's how to diagnose and fix HTTP 500 errors.
HTTP 500 Internal Server Error means the server crashed, threw an unhandled exception, or otherwise failed to process the request. Unlike 404 (resource not found) or 400 (bad request), 500 is always the server’s fault — the client did nothing wrong.
Use the HTTP Status Codes reference for a complete guide to all HTTP response codes.
What causes HTTP 500 errors
The 500 status code is a catch-all for any server-side failure that doesn’t have a more specific code. Common causes:
Unhandled exceptions: The application threw an error that wasn’t caught:
// This throws an uncaught TypeError if user is null:
app.get('/users/:id', async (req, res) => {
const user = await db.getUser(req.params.id);
res.json(user.profile); // TypeError if user is null → 500
});
Database errors: Query failed, connection pool exhausted, deadlock:
SQLSTATE[HY000]: General error: Deadlock found when trying to get lock
File system errors: Can’t read a file, disk full, permission denied:
Error: ENOENT: no such file or directory, open '/app/config.json'
Memory errors: Out of memory, segmentation fault in a native module.
Configuration errors: Missing environment variable, misconfigured server.
Dependency failures: External service returned an unexpected response that crashed the handler.
500 vs other 5xx codes
| Code | Name | Meaning |
|---|---|---|
| 500 | Internal Server Error | General server failure |
| 501 | Not Implemented | Server doesn’t support the request method |
| 502 | Bad Gateway | Upstream server returned invalid response |
| 503 | Service Unavailable | Server temporarily overloaded or in maintenance |
| 504 | Gateway Timeout | Upstream server didn’t respond in time |
502/503/504 typically indicate infrastructure problems (load balancer, proxy, upstream service). 500 indicates application code failures.
How to debug a 500 error
Step 1: Check the server logs
The 500 response goes to the client; the actual error goes to the server logs. Always check logs first:
# Node.js / Express:
tail -f /var/log/app/error.log
# Nginx:
tail -f /var/log/nginx/error.log
# Apache:
tail -f /var/log/apache2/error.log
# Docker:
docker logs container_name
# PM2 (Node.js process manager):
pm2 logs app-name --err
The error message, stack trace, and timestamp tell you exactly what went wrong.
Step 2: Reproduce locally
Reproduce the exact request that caused the 500:
# Using curl to replicate:
curl -X POST https://api.example.com/endpoint \
-H 'Content-Type: application/json' \
-d '{"key": "value"}'
Add detailed logging around the failure point to narrow down the cause.
Step 3: Check recent deployments
The most common cause of a 500 that wasn’t there yesterday: a deployment from today. Check what changed:
git log --oneline -10 # Recent commits
git diff HEAD~1 # Changes in last commit
Step 4: Check external dependencies
If the 500 started at a specific time, correlate with:
- Database connection limits reached
- Third-party API started returning errors
- Memory or disk usage spikes
- Scheduled job or batch process started at that time
Returning 500 correctly in code
Express (Node.js):
// Global error handler — catches all unhandled errors:
app.use((err, req, res, next) => {
console.error('Unhandled error:', err);
// Don't expose error details to clients in production:
const message = process.env.NODE_ENV === 'production'
? 'Internal server error'
: err.message;
res.status(500).json({ error: 'internal_server_error', message });
});
// Always use try/catch in async handlers:
app.get('/data', async (req, res, next) => {
try {
const data = await fetchData();
res.json(data);
} catch (err) {
next(err); // Pass to error handler
}
});
FastAPI (Python):
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
import logging
app = FastAPI()
logger = logging.getLogger(__name__)
@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):
logger.error(f"Unhandled exception: {exc}", exc_info=True)
return JSONResponse(
status_code=500,
content={"error": "internal_server_error", "message": "An unexpected error occurred"}
)
What NOT to do with 500 errors
Don’t expose stack traces to clients:
// NEVER return this to API clients:
{
"error": "TypeError: Cannot read properties of null (reading 'email')",
"stack": "TypeError: Cannot read properties...\n at /app/routes/users.js:45:32\n at..."
}
Stack traces reveal:
- Technology stack (Node.js, Python, Java)
- File paths and code structure
- Database query structure in some cases
- Third-party library versions
All of this helps attackers profile your system. Log the stack trace server-side; return only a generic message to clients.
Don’t silently swallow errors:
// BAD — error disappears:
try {
await riskyOperation();
} catch (e) {
// Silently ignore
}
// GOOD — log and re-throw or return error:
try {
await riskyOperation();
} catch (e) {
logger.error('riskyOperation failed:', e);
throw e; // Let it become a 500 with proper logging
}
Monitoring for 500 errors
Set up alerts for sustained 500 error rates:
- Error rate above 1% over 5 minutes → alert
- Any 500 from a health check endpoint → alert
Tools: Datadog, New Relic, Sentry, Grafana with Prometheus.
Related tools
- HTTP Status Codes — complete reference for all HTTP codes
- HTTP 404 Not Found — the most common client error
- HTTP 422 vs 400 — request validation errors
Related posts
- When to Use 422 vs 400 (and Other HTTP Status Code Debates) — 400 means the request is malformed. 422 means it's valid but semantically wrong.…
- HTTP 301 vs 302 Redirect — Permanent vs Temporary Redirects Explained — Understand when to use 301 (permanent) vs 302 (temporary) redirects, how they af…
- HTTP 404 Not Found — What It Means and How to Fix It — HTTP 404 means the server understood the request but can't find the resource. It…
Related tool
Full HTTP status code reference with explanations and when to use each.
Written by Mian Ali Khalid. Part of the Dev Productivity pillar.