5xx server errors
The server broke
4xx means the client did something wrong. 5xx means the server did something wrong. The client sent a perfectly valid request, and the server could not handle it. These are the status codes you never want to see in your logs, but they happen, and how you handle them matters.
500 Internal server error
This is the catch-all. Something went wrong on the server, and it does not have a more specific explanation. Maybe an unhandled exception was thrown. Maybe a database query failed. Maybe there is a bug in the code.
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
{"error":{"code":"INTERNAL_ERROR","message":"An unexpected error occurred"}} Notice the response says “an unexpected error occurred” and nothing more. That is intentional. You never want to send the real error message or stack trace to the client. That would leak information about your server’s internals. The actual details belong in your server’s logs, where your team can see them but the outside world cannot.
In the Error Handling course, the global error handler returns 500 for programmer errors like TypeError and ReferenceError. These are bugs the server did not anticipate.
502 Bad gateway
This one shows up when your server is sitting behind something else (like Nginx or a load balancer) and that thing cannot reach the server. Your Node.js process crashed, or it is not running, or it is not responding.
HTTP/1.1 502 Bad Gateway You see 502 when: Nginx cannot reach your Node.js server, a load balancer’s backend is down, or your server called an external API that returned something the proxy could not understand.
The client should retry after a short delay. The upstream server might recover.
503 Service unavailable
The server is temporarily unable to handle requests. It is alive but overloaded, in maintenance mode, or a critical dependency (like the database) is down.
HTTP/1.1 503 Service Unavailable
Retry-After: 30 The Retry-After header tells the client how long to wait. This is the server’s way of saying “I am not dead, just busy. Come back in 30 seconds.”
[!NOTE] The Error Handling course uses 503 for circuit breakers (when a dependency is down) and health checks (when the app is unhealthy).
What should clients do with 5xx errors?
Retry with backoff. 5xx errors are often temporary. The server was overloaded, a dependency blipped, or a deploy was in progress. Wait a few seconds and try again. If it still fails, wait longer and try again. This pattern is called exponential backoff, and it is the standard approach.
Respect Retry-After. If the server tells you to wait 30 seconds, wait 30 seconds. Retrying immediately adds more load to a server that is already struggling.
Do not retry 4xx errors. A 400 or 404 will not change on retry. The request itself is wrong. Only retry 5xx errors and network failures.
The most important distinction in HTTP error handling
| Code | Whose fault? | Should retry? |
|---|---|---|
| 4xx | Client | No (fix the request) |
| 5xx | Server | Yes (after a delay) |
4xx means “you sent a bad request, fix it.” 5xx means “I broke, try again later.” If you remember nothing else about status codes, remember this.
That wraps up status codes. We have covered 2xx (success), 3xx (redirect), 4xx (client error), and 5xx (server error). The next section goes deeper into something we have been seeing in every request and response: headers.
Exercises
Exercise 1: Throw an unhandled error in a route handler. Verify the response is 500 with a generic message (not the actual error details).
Exercise 2: Build a route that returns 503 with a Retry-After header. Parse the header in client code.
Exercise 3: Build a client that retries on 5xx but not on 4xx.
Should a client retry a 500 Internal Server Error?