Skip to main content
Adopt

IPA-114: Errors

Effective error communication is an important part of designing simple and intuitive APIs. Services returning standardized error responses enable API clients to construct centralized common error-handling logic. This common logic simplifies API client applications and eliminates the need for cumbersome custom error-handling code.

Guidance

  1. APIs must return ApiError when errors occur

    {
    "error": 400,
    "reason": "Bad Request",
    "detail": "The request content produced validation errors.",
    "errorCode": "BAD_REQUEST",
    "parameters": []
    }
    Why:

    The error response uses the standardized ApiError format, letting clients build common error-handling logic.

  2. APIs should avoid unexpected errors (5XX) by correctly handling validations and exposing the appropriate error user error (4XX)

    {
    "error": 400,
    "reason": "Bad Request",
    "detail": "The request content produced validation errors.",
    "errorCode": "BAD_REQUEST",
    "parameters": []
    }
    Why:

    A malformed request surfaces as a 400 Bad Request client error rather than an unexpected 5XX server error.

    1. Exercise the operation with invalid and edge-case input.

    2. Confirm validation failures and other client-caused conditions return an appropriate 4XX status rather than a 5XX.

    3. Flag operations that return 5XX for conditions that should have been caught and reported as client errors.

  3. Errors must use the canonical error codes allowed for the errorCode field

    {
    "error": 400,
    "reason": "Bad Request",
    "detail": "The request content produced validation errors.",
    "errorCode": "BAD_REQUEST",
    "parameters": []
    }
    Why:

    The errorCode value BAD_REQUEST is drawn from the canonical set of allowed error codes.

  4. APIs may make the best effort to help customers with possible next steps in case of an error by adding a help field

    • help must include a short description as description
    • help must include a link to the documentation url
  5. Methods must document any possible error and their associated HTTP status code

    paths:
    /groups/{groupId}/clusters/{clusterName}:
    get:
    operationId: getGroupCluster
    responses:
    "200":
    description: The cluster.
    "404":
    description: The cluster was not found.
    "401":
    description: The client lacks valid credentials.
    Why:

    The operation documents each error it can return alongside its HTTP status code, so clients know what to handle.

    1. For each operation, enumerate the errors it can return.

    2. Confirm every possible error is documented with its associated HTTP status code in the responses.

    3. Flag operations that can return errors that are not documented.

  6. Authentication and Authorization

  7. APIs must document 401 Unauthorized and 403 Forbidden status codes for endpoints that require authentication.

    paths:
    /groups/{groupId}/clusters/{clusterName}:
    get:
    operationId: getGroupCluster
    responses:
    "401":
    description: The client lacks valid credentials.
    "403":
    description: The client does not have permission for the resource.
    Why:

    The authenticated endpoint documents both 401 Unauthorized and 403 Forbidden, so clients know how authentication and authorization failures are reported.

    1. Identify endpoints that require authentication.
    2. Confirm each documents both the 401 Unauthorized and 403 Forbidden status codes.

    3. Flag authenticated endpoints missing either status code.

  8. APIs must return 401 Unauthorized if the client lacks valid credentials.

    {
    "error": 401,
    "reason": "Unauthorized",
    "detail": "The request lacks valid authentication credentials.",
    "errorCode": "UNAUTHORIZED",
    "parameters": []
    }
    Why:

    A request without valid credentials receives 401 Unauthorized, the status code for missing or invalid authentication.

    1. Call an authenticated endpoint with missing or invalid credentials.

    2. Confirm the response status code is 401 Unauthorized.

    3. Flag endpoints that respond with a different status code when credentials are absent or invalid.

  9. APIs must return 403 Forbidden if the client is authenticated but does not have permission to access the resource.

    {
    "error": 403,
    "reason": "Forbidden",
    "detail": "The client does not have permission to access this resource.",
    "errorCode": "FORBIDDEN",
    "parameters": []
    }
    Why:

    An authenticated client without permission for the resource receives 403 Forbidden, distinguishing authorization failure from missing authentication.

    1. Call an endpoint as an authenticated client that lacks permission for the resource.

    2. Confirm the response status code is 403 Forbidden.

    3. Flag endpoints that respond with a different status code in this case.

  10. Not Found

  11. APIs must document the 404 Not Found status code when the resource identifier includes one or more resource IDs.

    paths:
    /groups/{groupId}/clusters/{clusterName}:
    get:
    operationId: getGroupCluster
    responses:
    "404":
    description: The cluster was not found.
    Why:

    The path includes resource IDs, so the operation documents 404 Not Found for the case where an ID does not resolve to a resource.

    1. Identify operations whose path includes one or more resource IDs.

    2. Confirm each documents the 404 Not Found status code.

    3. Flag such operations that omit 404 Not Found.
  12. APIs must return a 404 Not Found status code if the client provides an invalid or non-existent ID

    {
    "error": 404,
    "reason": "Not Found",
    "detail": "No cluster matches the provided identifier.",
    "errorCode": "NOT_FOUND",
    "parameters": []
    }
    Why:

    A request for an invalid or non-existent ID returns 404 Not Found rather than a success or a 400 Bad Request.

    1. Call an operation with an invalid or non-existent resource ID.

    2. Confirm the response status code is 404 Not Found.

    3. Flag operations that respond with a different status code for invalid or non-existent IDs.

  13. tip

    For path parameters consider applying regex-based routing to ensure invalid IDs are treated as 404 Not Found rather than 400 Bad Request.

    Validation Errors

    Validation errors occur when the client sends a request that does not meet the API's requirements. Proper validation is critical for maintaining data integrity and providing clear feedback to clients.

    Validation errors typically occur when:

    • Required fields are missing
    • Data types don't match expected formats
    • Values fall outside acceptable ranges
    • Business rules are violated
  14. APIs must return a 400 Bad Request status code when validation fails

    {
    "error": 400,
    "reason": "Bad Request",
    "detail": "The request content produced validation errors.",
    "errorCode": "BAD_REQUEST",
    "parameters": []
    }
    Why:

    A request that fails validation returns 400 Bad Request, telling the client the request itself was invalid.

    1. Submit a request that violates the operation's validation requirements.

    2. Confirm the response status code is 400 Bad Request.

    3. Flag operations that respond with a different status code when validation fails.

  15. APIs must not accept invalid requests and silently modify field values to make them valid

    • This violates the principle that client-owned fields must not be modified by the server (see IPA-111)
    {
    "error": 400,
    "reason": "Bad Request",
    "detail": "The request content produced validation errors.",
    "errorCode": "BAD_REQUEST",
    "parameters": []
    }
    Why:

    The API rejects an invalid request with 400 Bad Request instead of silently adjusting client-owned field values to make the request valid.

    1. Submit a request with an invalid value for a client-owned field.

    2. Confirm the request is rejected rather than accepted with the value silently modified by the server.

    3. Flag operations that coerce or overwrite invalid client-owned field values instead of rejecting the request.

  16. APIs should make the best effort to validate as much as possible of the request and include all validation errors in the field badRequestDetail

    • badRequestDetail must include an array of fields and each field must include:
      • the error description as description
      • field with errors as field
    {
    "error": 400,
    "reason": "Bad Request",
    "detail": "The request content produced validation errors.",
    "errorCode": "BAD_REQUEST",
    "parameters": [],
    "badRequestDetail": {
    "fields": [
    {
    "description": "must not be null",
    "field": "groupId"
    },
    {
    "description": "must not be empty",
    "field": "authors[0].name"
    }
    ]
    }
    }
    Why:

    The response collects every validation failure in badRequestDetail.fields, each entry naming the offending field and its description, so the client can fix all problems at once.

    1. Submit a request that violates several validation requirements at once.

    2. Confirm the response reports all of the violations in badRequestDetail, with each field providing both description and field.

    3. Flag operations that report only the first validation error or omit the required description or field entries.

  17. Rate Limiting

  18. APIs must document the 429 Too Many Requests status code for endpoints that implement rate limiting.

    paths:
    /groups/{groupId}/clusters:
    get:
    operationId: listGroupClusters
    responses:
    "429":
    description: The client has exceeded the allowed request rate.
    Why:

    The rate-limited endpoint documents 429 Too Many Requests, so clients know how exceeding the request rate is reported.

    1. Identify endpoints that implement rate limiting.

    2. Confirm each documents the 429 Too Many Requests status code.

    3. Flag rate-limited endpoints that omit 429 Too Many Requests.

  19. APIs must return 429 Too Many Requests when a client exceeds the allowed request rate.

    {
    "error": 429,
    "reason": "Too Many Requests",
    "detail": "The client has exceeded the allowed request rate.",
    "errorCode": "TOO_MANY_REQUESTS",
    "parameters": []
    }
    Why:

    When a client exceeds the allowed request rate, the API returns 429 Too Many Requests rather than continuing to serve or returning another status.

    1. Issue requests to a rate-limited endpoint until the allowed request rate is exceeded.

    2. Confirm the response status code is 429 Too Many Requests.

    3. Flag rate-limited endpoints that respond with a different status code once the rate is exceeded.

  20. APIs should include the Retry-After HTTP response header when returning 429 Too Many Requests to indicate how long the client should wait before retrying the request.

    • The Retry-After header value must be expressed as time in seconds until the next request can be made.
    HTTP/1.1 429 Too Many Requests
    Retry-After: 30
    Why:

    The 429 Too Many Requests response includes Retry-After: 30, telling the client to wait 30 seconds before retrying.

    1. Trigger a 429 Too Many Requests response from a rate-limited endpoint.

    2. Confirm the response includes a Retry-After header whose value is a time in seconds.

    3. Flag 429 responses that omit Retry-After or express it in units other than seconds.

  21. APIs should include rate limit information in response headers to help clients manage their request rates proactively:

    • RateLimit-Limit: The maximum number of requests allowed in the current rate limit window
    • RateLimit-Remaining: The number of requests remaining in the current rate limit window
    HTTP/1.1 200 OK
    RateLimit-Limit: 100
    RateLimit-Remaining: 73
    Why:

    The response exposes RateLimit-Limit and RateLimit-Remaining, letting the client see its budget and pace requests before hitting the limit.

    1. Call a rate-limited endpoint within the allowed rate.

    2. Confirm the response includes the RateLimit-Limit and RateLimit-Remaining headers.

    3. Flag rate-limited endpoints that do not surface this rate limit information in response headers.

API Error Format

{
"error": 400, // HTTP status code (required)
"reason": "Bad Request", // Human-readable error message (optional)
"detail": "The request content produced validation errors.", // Detailed description (optional)
"errorCode": "BAD_REQUEST", // Application-specific error code (optional)
"parameters": [], // Array of additional parameters (optional)
"badRequestDetail": {
// Only present for validation errors (optional)
"fields": [
{
"field": "Request body", // Path to the problematic field
"description": "must not be null" // Description of the violation
}
]
}
}

Example

{
"badRequestDetail": {
"fields": [
{
"description": "must not be null",
"field": "groupId"
},
{
"description": "must not be empty",
"field": "authors[0].name"
}
]
},
"detail": "The request content produced validation errors.",
"error": 400,
"errorCode": "BAD_REQUEST",
"help": {
"description": "troubleshooting documentation",
"url": "https://www.mongodb.com/docs/atlas/reference/api-errors/"
},
"parameters": [],
"reason": "Bad Request"
}
tip

New fields badRequestDetail and help are inspired by googleapis/google/rpc/error_details.proto