Error Handling
Error response structure, HTTP error codes, rate limit headers, and streaming errors.
This guide covers error handling for Copilot via API.
Response Structure
All error responses share a consistent JSON structure:
{
"error_code": "<string>",
"detail": "<string>"
}error_code— a stable string identifier for the error type. Use this in your error handling logic.detail— a human-readable description. Treat this as informational; do not rely on it programmatically.
This structure is returned for all errors, including unexpected ones. If an unhandled error occurs, the API returns HTTP 500 with a generic error_code and detail rather than exposing internal details.
HTTP Errors
The table below lists the possible HTTP status codes, their meanings, and example response bodies.
| HTTP Status | Meaning | Response |
|---|---|---|
| 400 | Bad Request - Content Filter | {"error_code": "content_filter_error", "detail": "Could not parse ..."} |
| 400 | Bad Request | {"error_code": "bad_request", "detail": "OpenAI API request was invalid."} |
| 400 | Length Filter | {"error_code": "length_finish_reason_error", "detail": "OpenAI length filter finished with an error."} |
| 401 | Unauthorized | {"error_code": "unauthorized", "detail": "Authentication credentials."} |
| 403 | Permission Denied | {"error_code": "permission_denied", "detail": "OpenAI API request was denied permission."} |
| 409 | Conflict | {"error_code": "conflict_error", "detail": "OpenAI API request resulted in a conflict."} |
| 422 | Unprocessable Entity | {"error_code": "unprocessable_entity", "detail": "OpenAI API request was unprocessable."} |
| 429 | Too Many Requests | {"error_code": "rate_limit_error", "detail": "OpenAI API request rate limit exceeded."} |
| 500 | API Timeout | {"error_code": "api_timeout_error", "detail": "OpenAI API request timed out."} |
| 500 | API Connection Error | {"error_code": "api_connection_error", "detail": "OpenAI API request connection error."} |
| 500 | API Error | {"error_code": "api_error", "detail": "OpenAI API request resulted in an error."} |
Example
{
"error_code": "content_filter_error",
"detail": "Could not parse response content as the request was rejected by the content filter"
}Rate Limit Errors (HTTP 429)
When you exceed a rate limit, the API returns HTTP 429 with the following body:
{
"detail": "Rate Limit Exceeded"
}The response also includes headers to help you implement retry logic:
| Limit Breached | Header | Value |
|---|---|---|
| Per-second | Retry-After-Milliseconds | Milliseconds until the current second window resets |
| Per-second | X-Rate-Limit-Second | Your allowed requests-per-second limit |
| Per-minute | Retry-After-Seconds | Seconds until the current minute window resets |
| Per-minute | X-Rate-Limit-Minute | Your allowed requests-per-minute limit |
Use the Retry-After-Milliseconds or Retry-After-Seconds header value to wait before retrying. Implement exponential backoff for repeated 429 responses.
For the applicable rate limits, see the Rate Limits guide.
Errors During Streaming
When an error occurs during a streaming response, the stream will include all chunks generated up to the point of the error, followed by a final chunk containing the error details and finish reason in the meta_data field.
Example
{
"message_id": "00000000-0000-0000-0000-000000000001",
"thread_id": "00000000-0000-0000-0000-000000000001",
"created_at": "2024-11-15T17:43:37.443794",
"delta": {
"role": "assistant",
"content": "Die",
"sources": [],
"meta_data": null
}
}
{
"message_id": "00000000-0000-0000-0000-000000000001",
"thread_id": "00000000-0000-0000-0000-000000000001",
"created_at": "2024-11-15T17:43:37.446459",
"delta": {
"role": "assistant",
"content": "",
"sources": [],
"meta_data": {
"type": "common_metadata",
"finish_reason": "content_filter_error",
"detail": "Could not parse response content as the request was rejected by the content filter."
}
}
}If an error occurs during streaming, the message is not persisted — even if you already received a message_id in the stream.