Of all the error codes, 500 Internal Server Error is the most generic — and that's exactly what makes it stressful. It doesn't tell you what broke. It just says: something failed inside the server, and it couldn't finish your request.
The good news: a 500 almost always leaves a trail. You just need to know where to look.
What a 500 means
A 500 is a server error (5xx): your application hit an unhandled problem while processing the request and gave up. Unlike a 502 (proxy got no valid response) or 503 (server too busy), a 500 means the app itself tried, threw an error, and couldn't recover.
500 = "my code broke." It's the catch-all for an exception the application didn't handle — which is why the fix is almost always in your logs.
Common causes
| Cause | Example |
|---|---|
| Unhandled exception | A null reference, a type error, a failed assertion |
| Database error | A failed query, lost connection, constraint violation |
| Bad configuration | A missing environment variable or secret |
| Failed dependency | An external call your code didn't guard |
| Code bug | A regression shipped in the last deploy |
| Out of resources | Out of memory or disk mid-request |
How to find the real error
The 500 page is deliberately vague (you don't want to leak internals to users). The real message is in your logs:
- Read the application logs. A 500 nearly always has a matching stack trace — that's your actual root cause, pointing at the exact line.
- Check what changed. A wave of 500s right after a deploy means the deploy did it — consider rolling back first, debugging second.
- Look at the database. Failed queries and dropped connections are a top source.
- Verify configuration. A missing secret or env var in one environment is a classic "works locally, 500 in prod."
- Check resources. Out-of-memory errors surface as sudden 500s.
How to prevent (most of) them
- Handle errors gracefully. Wrap risky operations; return a clean error instead of an unhandled exception.
- Validate inputs. A lot of 500s are really unvalidated input that should've been a 400.
- Test before shipping. Most 500s in production are regressions a test would've caught.
- Make rollback fast. When a deploy causes 500s, the quickest cure is reverting.
- Monitor for spikes. A jump in 500s is the clearest signal of a real incident — catch it before customers report it.
A note on what users see
Never show a raw stack trace to users — it's ugly and a security risk. Serve a friendly 500 page (like a good 404 page) while logging the full detail privately for yourself.
The bottom line
| In one line | |
|---|---|
| What | The app hit an unhandled error and couldn't finish. |
| vs 502/503 | 500 = your code broke; 502 = dead upstream; 503 = too busy. |
| Find it | Read the logs — the stack trace is the cause. |
| Prevent | Handle errors, validate input, test, fast rollback, monitor. |
A 500 is vague to users on purpose — but it's specific in your logs. Follow the stack trace, check the last deploy, and treat a spike of 500s as the incident it usually is.
More: 502 Bad Gateway, HTTP status codes explained, and web monitoring.