HTTP 500

500 Internal Server Error

Server Error

The server encountered an unexpected condition that prevented it from fulfilling the request. 500 is intentionally generic. It does not commit to the cause, and the response body should not leak stack traces or internal paths to clients. A well-built 500 handler logs the full error with a request ID server-side and returns the request ID to the client so support and engineering can correlate later. 500 is the right code for unhandled exceptions, database connection failures during a request, and any condition where the server can no longer reason about whether the request would have succeeded.

When does this happen?

Return 500 when an unexpected condition fires that the application did not anticipate. The canonical trigger is an unhandled exception in business logic. A null dereference, a divide-by-zero, an unexpected schema mismatch. Other causes include database connection failures mid-request, out-of-memory conditions, panics in lower-level code, and assertion failures. The response should be structured so clients can detect the error programmatically (an error code or request ID) without exposing the stack trace publicly. Search engines treat 500 as a transient condition by default. They will retry the URL. But persistent 500s on indexed pages eventually cause de-indexing. Browsers display a generic error or whatever HTML the server happened to return. Watch for 500s emitted by middleware that runs before your application. Load balancers, WAFs, and reverse proxies sometimes return 500 when they cannot reach the application; the cause is upstream of your code.

Common causes

How to fix it

Real-world examples

New code path divides by a count that turns out to be zero in production.
Application throws ZeroDivisionError, returns 500 with a request ID. Logs show the stack trace pointing at the exact line.
Database connection pool is sized at 20 and a slow query has tied up every connection for two minutes.
New requests wait for a connection, eventually time out, and the handler returns 500. Operator scales up the pool or kills the slow query.
Migration added a column but the production deploy of the application code happened before the migration ran.
Queries reference the missing column and the database raises an error. Application returns 500 until the migration completes.
Disk fills up at 3am because log rotation broke.
Every request that tries to write a log line fails and the application returns 500. Operator clears space and rotates logs.
Unhandled promise rejection in a Node service causes the request handler to never resolve.
Framework's catch-all middleware fires and returns 500. Engineer adds explicit error handling in the offending handler.
Race condition in a caching layer corrupts a value once per ten thousand requests.
Affected requests deserialize the corrupted value, throw an exception, and return 500. Distributed traces show the corruption originating in the cache write.

Debugging

How it differs from related codes

HTTP 502

500 means the application itself crashed or errored. 502 means a gateway or proxy could not get a valid response from an upstream. The upstream may have crashed but the symptom is at the gateway layer.

HTTP 503

503 is a deliberate "we are unavailable". Maintenance, overload, planned downtime. 500 is an unexpected error. 503 is recoverable on its own; 500 needs investigation.

HTTP 504

504 is a timeout against an upstream server. 500 is a generic application error. If your app called another service that did not respond, the error you saw was probably 504 internally, surfaced as 500 to the client.

Related status codes

400 408 422 502 503 504

See HTTP 500 in your redirect chains?

Check your URLs with checkredirects.io