Go errors: sentinel errors, error structs or fmt.Errorf — how to choose
Code review on ClaudeGate. The HTTP handler that receives execution jobs was returning a 500 when the job queue was full. A colleague caught it in the logs: "this isn't an internal error, it's satu...

Source: DEV Community
Code review on ClaudeGate. The HTTP handler that receives execution jobs was returning a 500 when the job queue was full. A colleague caught it in the logs: "this isn't an internal error, it's saturation — it should be a 503." He was right. The problem was a single innocent line: all errors from Enqueue treated the same way, without distinguishing their nature. That's the moment you have to ask the real question: how do you structure errors so the caller can respond correctly? The concrete bug — 500 instead of 503 The original code in the HTTP handler looked like this: // ❌ Before — any enqueue error returns 500 if err := h.queue.Enqueue(j.ID); err != nil { writeError(w, http.StatusInternalServerError, "failed to enqueue job") return } "Queue full" and "internal error" are not the same thing. A server with a full queue is working correctly — it's temporarily saturated. A client receiving 500 assumes the server is broken and probably won't retry. A client receiving 503 Service Unavailab