Best practices for API error handling and troubleshooting

Before starting

OAuth 2.0 defines an authorization protocol for securing application access to protected resources provided by our Orange APIs. OAuth 2.0 relies on access tokens presented by client applications when requesting access to protected resources via APIs. These tokens must be obtained before the client application can get access to these resources.

For each API, rate limiting policies are enforced in order to

  • protect the API against severe traffic spikes and denial of service attacks,
  • limit the number of API calls apps can make to the API over a specific fixed or rolling window (e.g. minute, hour, day, week or month).

This document provides details about error cases to be managed by your application, and the error codes and explanations you shall refer to when troubleshooting errors.It also provides details on the request ID sent back to your application for any successful or failed requests to protected resources, and that may be used to report problem to our support team.

Error management

Error reporting

HTTP status code

Orange APIs use appropriate HTTP response status codes to indicate whether a specific HTTP request has been successfully completed or not. Only two classes of error codes are defined by HTTP/1.1 protocol:

  • client errors (4xx)
  • servers errors (5xx)

The table below provides the list of the main HTTP status codes that could be returned by our Orange APIs. In the current release of this document, we'll first focus on the three following code: 401, 403 and 503.

Status codDescription
400Is returned when the request entity sent by your application could not be understood by the server due to malformed syntax (e.g. invalid payload, data type mismatch)
401Is returned when there is a problem with the credentials provided by your application. This code indicates that your application tried to operate on a protected resource without providing the proper authorization. It may have provided the wrong credentials or none at all
403Is returned when your application is not authorized to access the requested resource, or when your application is being rate limited
404Is returned when the resource requested by your application does not exist
405Is returned when the HTTP method used by your application is not allowed for the resource
406Is returned when the resource requested by your application is not capable of generating response entities that are compliant the Accept headers sent
408Is returned when your application did not produce a request within the time that the server was prepared to wait
409Is returned when the request sent by your application could not be completed due to a conflict with the current state of the resource
415Is returned when the request entity sent by your application is in a format not supported by the requested resource for the requested method
500Is returned when the server encountered an unexpected condition which prevented it from fulfilling the request sent by your application
503Is returned when the server is currently unable to handle the request sent by your application due to a temporary overloading or maintenance of the server

Please note that

  • HTTP status codes are normative, but the status messages are not. This means that servers are free to use application-specific error message strings.
  • A response with an HTTP error code or a connection time-out does not imply that the request message has not been treated by the server.

A more technical breakdown of HTTP 1.1 status codes and their meanings is available at https://tools.ietf.org/html/rfc7231#page-47.

Error response format

Error response messages provide additional information about the underlying fault, that you can use to debug the error as well as providing user friendly feedback in your application. The error reporting is designed to make the APIs usable - easy to implement and debug.

The body of the response contains more details about the error. The body is JSON formatted like regular responses. Errors are guaranteed to remain unchanged when using a specific API version.

Error response entity for errors have an HTTP status code (e.g. 401 Unauthorized) and up to four attributes - two mandatory and two optional as shown below:

FieldTypeDescription
codeintegerAn integer coding the error type
messagestringA short localized string that describes the error
descriptionstring(optional) A long localized error description if needed. It can contain precise information about which parameter is missing, or what are the acceptable values
infoURLURL(optional) A URL to online documentation that provides more information about the error

As an example:

HTTP/1.1 401 Unauthorized
Content-Type: application/json

{
  "code": 41,
  "message": "Invalid credentials",
  "description": "The requested service needs credentials, but the ones provided were invalid."
}

Error handling

On server side, Orange APIs handle errors by aborting the request and immediately returning an error response to your application.

  • Requests that result in errors because of bad client behavior can be re-attempted after fixing the underlying fault or after a given delay (for instance when being rate limited).
  • Requests that fail because of server side failures are indicated accordingly in the response.

The table below provides details about the main error cases that your application could meet.

HTTP StatusError codeRoot causesHow to handle
40140Missing credentials: e.g. missing Authorization header, missing token, missing Bearer prefix...Verify Authorization header is present, with valid Bearer token
40141Invalid credentials: e.g. invalid client_id and/or client_secret, pending or revoked client_id...Check your app's credentials on the 'Application details' page of our developer portal. You may have renewed your credentials
40142Expired credentials: e.g. expired OAuth 2.0 token (> 3 month)Generate a new token (i.e. POST /token) for your application. See See Tips & Tricks section
40350Access denied: e.g. not subscribed API, pending or revoked API subscriptionCheck your app's subscriptions on the 'Application details' page of our developer portal
40353Too many requests: e.g. abnormal peak load on our Orange API, application quota defined by our API offer is exceeded (e.g. max. 1500 request/month/app)See Tips & Tricks section
5035Unavailable Orange API: e.g. bottleneck in the network, server is too busy or there is maintenance being performed on itCheck API status if availale (GET /status). If the problem persist, contact our support team (see Troubleshooting section)

Tips and tricks

OAuth 2.0 token caching

When requesting an OAuth access token using the POST /token operation, your application shall exploit the 'expire_in' field that provides the period of validity for the access token, expressed in seconds (e.g. 7776000 -> 90 days). Your application should cache this token and do not request a new access token until receiving an error indicating that the access token has expired.

HTTP/1.1 200 OK
Content-Type: application/json
{
  "token_type": "Bearer",
  "access_token": "i6m2iIcY0SodWSe...L3ojAXXrH",
  "expires_in": "7776000"
}

Rate limited application

Two types of quota could make your application be rate limited, depending on our Orange APIs configuration:

  • Rate limiting quota is set for all our APIs.
  • Application quota is optional.

1/ Rate limiting quota

For protecting Orange APIs against abnormal peaks of traffic, we configured a maximum number of allowed requests per second, for all inbound traffic made by applications - i.e. max. 25 requests per second. When a new request comes in, the server determines if the rate limit is exceeded for the current window of time (i.e. second). If so, the request is rejected on error, until the quota counter resets.

The following error message is returned to your application:

HTTP/1.1 403 Forbidden
Content-Type: application/json

{
  "code": 53,
  "message": "Too Many Requests",
  "description": "The application has made too many calls and has exceeded the spike arrest limit for this service."
}

When rate limited, your application should wait a short delay (in the range of second) before retrying to send the request. In Java, the following commands could be used:

TimeUnit.SECONDS.sleep(1);
OR Thread.sleep(1000);

2/ Application quota

Let's consider the two following examples:

  1. for a given API, we configured the maximum number of allowed requests per fixed window - i.e. max. 2500 requests per month. When a new request comes in, the server determines if the quota has been exceeded for your application during the time window. If so, the request is rejected on error, until the quota counter resets, at midnight GMT of the last day of the month.
  2. for an other API, we configured the maximum number of allowed requests per rolling window - i.e. max. 600 requests per minute. When a new request comes in, the server determines if the quota has been exceeded for your application during the past time window. If so, the request is rejected on error. The counter never resets, but is recalculated on each request.

In both cases, the following error message will be returned to your application. Compared to section 1/, please note that the error code/message are the same, but that the description message changed ('global quota limit' instead of 'spike arrest limit').

HTTP/1.1 403 Forbidden
Content-Type: application/json
{
  "code": 53,
  "message": "Too Many Requests",
  "description": "The application has made too many calls and has exceeded the global quota limit for this service."
}

On client side, your application must process the response entity to extract the information about the error that occurred (i.e. HTTP status code, error code, error message, error description (optional)).

In practice, frameworks will help you. A good example is Spring MVC’s RestTemplate. By default, the RestTemplate throws an exception for any response in the 4xx or 5xx ranges. You can customize this by providing your own implementation of ResponseErrorHandler. The default error handling behavior often makes sense, as it prevents you from checking the status code after each request. You can let an exception interrupt the execution flow, as usual.

Troubleshooting

In distributed service/resource-oriented architecture, request IDs are a way of grouping all the information associated with a given request to a protected resource; the main benefits are two-fold:

  1. Provides a tagging mechanism for events that are produced, so that a full report of what occurred and timing in every component touched can be generated for troubleshooting purpose
  2. Exposes an identifier to developers, which can be used to track down specific issues that they’re running into. If a request is consistently failing and the developer has verified that the request is properly formulated, the request ID may be used to report a problem to the support team.

Every request made against Orange APIs returns a response header named X-OAPI-Request-Id. This header contains an opaque value that uniquely identifies the request.

As an example:

HTTP/1.1 503 Internal Server Error
Date: Thu, 14 Sep 2017 06:49:12 GMT
Content-Type: application/json
Content-Length: 184
X-OAPI-Request-Id: opopecballrt02-31151-13125058-1

{
  "code": 5,
  "message": "The service is temporarily unavailable",
  "description": "The service in charge of the requested endpoint is temporarily unavailable or unreachable."
}

If a request is consistently failing and you have verified that the request is properly formulated, you may use this value to report the error to Orange as explained below.

  1. Go to Contact us page
  2. Select 'Request type': API Incident -> There is a problem with an API used by my application
  3. Select the relevant option
  4. Fill in the request details

In your support request, include information about the type of operation that the request attempted (e.g. POST https/api.orange/com/cloud/v1/folders -> create a new folders into Orange customer's personal cloud), as well as the value of X-OAPI-Request-Id header and the approximate time that the request was made (e.g. Thu, 14 Sep 2017 06:49:12 GMT).