500 Internal Server Error means the server hit an unexpected condition and couldn't complete the request. It's the generic "something broke" code: the server knows it failed but isn't telling you why. The real reason is almost always an exception or error in the application's logs.
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
{"error": "internal_error", "message": "An unexpected error occurred", "request_id": "req_8f2c1a"}
Common causes
- An unhandled exception. A null reference, a type error or a missing key in a code path nobody tested.
- A failing dependency. The database is down, a query times out, or a downstream API call throws and isn't caught.
- Configuration. A missing environment variable, a bad secret, a wrong file path, or a syntax error in
.htaccessor another server config file. - Permissions. The process can't read a file or write to a directory it needs.
- Unexpected input. A request with a shape the code didn't expect triggers a crash. This is still a server bug: bad input should get a 4xx.
How to fix it as a visitor
Reload the page once, then wait a few minutes. If it keeps happening on the same action, report it to the site with the time and what you were doing. A 500 is a server-side bug, and clearing your cache won't fix it.
How to fix it as a developer or ops
- Read the logs at the time of the request. Your app's stderr,
journalctl -u myapp,kubectl logs, or your error tracker. Look for the stack trace. Logging a request ID in both the response and the log line makes this fast. - Reproduce it. Replay the exact request with
curl, including headers and body. Many 500s only happen with a specific payload. - Check what changed. A recent deploy, config change, dependency upgrade or database migration is the usual suspect.
- Check dependencies. Is the database reachable? Are connection pools exhausted? Is a downstream API failing?
- Turn on detailed errors locally only. Debug pages (
DEBUG=True,display_errors) help in development but leak internals in production.
On Apache and PHP hosts, a broken .htaccess rule or a PHP fatal error is the most common source of a bare 500. The Apache error_log names the line.
500s on a webhook endpoint
When your webhook handler throws, the provider sees a 500 and treats the delivery as failed. Most providers retry 5xx responses with backoff, but only for a limited window, and some don't retry at all. If the bug takes a day to fix, events sent during that day may be gone.
Two habits help:
- Return 5xx only for problems that a retry can fix, such as a database outage. If the signature is invalid or the payload is malformed, return a 4xx so the provider stops retrying a request that will never succeed.
- Acknowledge fast and process later. Store the raw event, return
200, and do the work from a queue. A bug in processing then never turns into a failed delivery.
If you put Webhook Relay in front of the endpoint, durable retries store each webhook first and retry it with exponential backoff for up to 30 days. Fix the bug, deploy, and the stored events are delivered. Delivery is at least once, so dedupe on the event ID (see idempotency). The webhook logs show the exact request and your endpoint's response for each attempt, which makes the failing payload easy to find.
When to return 500 from your own API
Return 500 when your server failed in a way the client couldn't have caused or prevented. Don't return it for validation errors (400 or 422), auth failures (401 or 403), or rate limits (429). When the outage is temporary and known, such as maintenance or overload, 503 Service Unavailable with Retry-After tells clients more. Never put stack traces in the response body. Return a request ID instead.
