Errors
Every error this library answers with is the same JSON envelope, and klhayyent decodes the same envelope on the way back in. Use it in your own handlers and every service in the chain speaks one error format.
The envelope
type ErrorResponse struct {
Code string `json:"code,omitempty"` // machine-readable, UPPER_SNAKE
Message string `json:"message,omitempty"` // for humans
Details any `json:"details,omitempty"` // whatever context helps
}
aichteeteapee.WriteJSON(w, http.StatusNotFound, aichteeteapee.ErrorResponseNotFound)
{
"code": "NOT_FOUND",
"message": "Not found"
}
WriteJSON(w, status, data) sets Content-Type: application/json, writes the status and encodes data with two-space indentation. An encoding failure is logged, since the status line is already gone by then.
To add details, copy a prebuilt value and set the field, the way EnforceRequestContentType does:
resp := aichteeteapee.ErrorResponseValidationFailed
resp.Details = map[string]string{"email": "required"}
aichteeteapee.WriteJSON(w, http.StatusUnprocessableEntity, resp)
Error codes
All codes are ErrorCode* constants in error_codes.go. The prebuilt ErrorResponse* values live in error_responses.go.
Status codes
ErrorCodeFromHTTPStatus(status) maps these statuses to their code. Any other status, 415 included, maps to INTERNAL_SERVER_ERROR.
| Status | Code | Prebuilt response | Message | Written by |
|---|---|---|---|---|
| 400 | BAD_REQUEST | ErrorResponseBadRequest | Bad request | |
| 401 | UNAUTHORIZED | ErrorResponseUnauthorized | Unauthorized | BasicAuth, BearerAuth (message configurable) |
| 403 | FORBIDDEN | ErrorResponseForbidden | Access forbidden | |
| 404 | NOT_FOUND | ErrorResponseNotFound | Not found | |
| 405 | METHOD_NOT_ALLOWED | ErrorResponseMethodNotAllowed | Method not allowed | FileUploadHandler |
| 409 | CONFLICT | ErrorResponseConflict | Conflict | |
| 410 | GONE | ErrorResponseGone | Gone | |
| 413 | PAYLOAD_TOO_LARGE | ErrorResponsePayloadTooLarge | Payload too large | |
| 422 | UNPROCESSABLE_ENTITY | ErrorResponseUnprocessableEntity | Unprocessable entity | |
| 429 | TOO_MANY_REQUESTS | ErrorResponseTooManyRequests | Too many requests | |
| 500 | INTERNAL_SERVER_ERROR | ErrorResponseInternalServerError | Internal server error | Recovery, upload postprocessor failures, directory listing failures |
| 501 | NOT_IMPLEMENTED | ErrorResponseNotImplemented | Not implemented | |
| 502 | BAD_GATEWAY | ErrorResponseBadGateway | Bad gateway | |
| 503 | SERVICE_UNAVAILABLE | ErrorResponseServiceUnavailable | Service unavailable | |
| 504 | GATEWAY_TIMEOUT | ErrorResponseGatewayTimeout | Gateway timeout | Timeout (with its own longer message) |
Library and application codes
These have no status mapping. The status in the table is the one the library uses when it writes them.
| Code | Prebuilt response | Message | Written by |
|---|---|---|---|
VALIDATION_FAILED | ErrorResponseValidationFailed | Validation failed | |
RATE_LIMITED | none | ||
ENDPOINT_NOT_FOUND | ErrorResponseEndpointNotFound | Endpoint not found | |
FILE_NOT_FOUND | ErrorResponseFileNotFound | File not found | Static files, 404 |
DIRECTORY_LISTING_NOT_SUPPORTED | ErrorResponseDirectoryListingNotSupported | Directory listing is not supported | Static files, 403 |
PATH_TRAVERSAL_DENIED | ErrorResponsePathTraversalDenied | Path traversal denied | Static files, 403 |
MISSING_USER_ID | ErrorResponseMissingUserID | User ID is required | |
INVALID_USER_ID | ErrorResponseInvalidUserID | Invalid user ID format | |
MISSING_CONTENT_TYPE | ErrorResponseMissingContentType | Content-Type header is required | EnforceRequestContentType, 400 |
UNSUPPORTED_CONTENT_TYPE | ErrorResponseUnsupportedContentType | Unsupported content type | EnforceRequestContentType and EchoHandler, 415 |
INVALID_MULTIPART_FORM | ErrorResponseInvalidMultipartForm | Invalid multipart form | FileUploadHandler, 400 |
NO_FILE_PROVIDED | ErrorResponseNoFileProvided | No file provided | FileUploadHandler, 400 |
FILE_SAVE_FAILED | ErrorResponseFileSaveFailed | Failed to save file | FileUploadHandler, 500 |
ErrorCode is an alias for string, so your own codes fit in the same field.
Sentinel errors
errors.go declares plain errors.New sentinels to match with errors.Is. The client returns the status ones from HTTPError.Unwrap, and the server returns the TLS ones from Start.
| Group | Sentinels |
|---|---|
| 4xx | ErrBadRequest, ErrUnauthorized, ErrNotAuthenticated, ErrForbidden, ErrNotFound, ErrMethodNotAllowed, ErrConflict, ErrGone, ErrUnprocessableEntity, ErrTooManyRequests |
| 5xx | ErrInternalServer, ErrBadGateway, ErrServiceUnavailable, ErrGatewayTimeout |
| Responses | ErrInvalidResponse, ErrEmptyResponse, ErrUnexpectedResponseStatus |
| Anti-bot | ErrBotDetected, ErrChallengeBlocked, ErrCaptchaRequired |
| API clients | ErrAPIError, ErrAPIKeyNotSet, ErrNilRequestBody |
| TLS | ErrTLSCertFileNotSpecified, ErrTLSKeyFileNotSpecified |
The client package adds its own: ErrInvalidBaseURL, ErrInvalidRequestTarget, ErrInvalidOption and ErrResponseBodyTooLarge.