> For the complete documentation index, see [llms.txt](https://developerdocs.instructure.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developerdocs.instructure.com/services/mastery-connect/errors.md).

# Errors

The error envelope, every error code, and how to react to each.

Every error — from any endpoint — returns the same envelope, so one handler covers the whole API.

## The Envelope

```json
{
  "errors": [
    {
      "status": "422",
      "code": "validation_error",
      "title": "Unprocessable Entity",
      "detail": "Unknown filter 'shcool_id'"
    }
  ]
}
```

| Field    | Use it for                                                |
| -------- | --------------------------------------------------------- |
| `status` | The HTTP status, as a string.                             |
| `code`   | **Branch on this.** A stable machine-readable identifier. |
| `title`  | The standard HTTP status name.                            |
| `detail` | Human-readable specifics. Helpful in logs; do not parse.  |

`errors` is always an array, though today it always holds exactly one entry. Treat it as a list so your parser survives multi-error responses later.

{% hint style="warning" %}
Branch on `code`, never on `detail`. Codes are part of the contract; `detail` strings are prose and may be reworded at any time.
{% endhint %}

## Status Codes

| Status | Meaning                                                |
| ------ | ------------------------------------------------------ |
| `200`  | Success.                                               |
| `401`  | Not authenticated — credentials or token problem.      |
| `403`  | Authenticated, but not allowed.                        |
| `404`  | No such record in your district.                       |
| `422`  | The request was understood but a parameter is invalid. |
| `429`  | Rate limit exceeded.                                   |
| `5xx`  | Server-side problem. Retry with backoff.               |

## Error Codes

### 401 — Authentication

| Code                 | Cause                                                                                   | What to do                                                                             |
| -------------------- | --------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
| `invalid_token`      | Missing, malformed, expired or revoked token; or bad credentials at the token endpoint. | Fetch a new token and retry **once**. If it fails again, the credential is bad — stop. |
| `expired_client`     | The credential has passed its expiry date.                                              | Not recoverable in code. Request a new credential.                                     |
| `revoked_credential` | The credential was revoked.                                                             | Not recoverable. Contact Support.                                                      |
| `unknown_district`   | The credential's district can no longer be resolved.                                    | Not recoverable. Contact Support.                                                      |
| `not_authorized`     | Authorisation failed for this request.                                                  | Check the token is being sent correctly.                                               |

### 403 — Forbidden

| Code                 | Cause                                             | What to do                                                                                                                    |
| -------------------- | ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `feature_disabled`   | The API is not enabled for this district.         | Contact Support to enable it. Retrying will not help.                                                                         |
| `insufficient_scope` | The token lacks the scope this endpoint requires. | Check the [scope table](/services/mastery-connect/authentication.md#scopes). On nested routes you need the **child's** scope. |
| `forbidden`          | Access denied to this resource.                   | Do not retry.                                                                                                                 |

### 404 — Not Found

| Code        | Cause                                                                                                                           |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `not_found` | No such record **in your district** — it may not exist, or it may belong to someone else. The API does not distinguish the two. |

An unknown report name also returns `not_found`.

### 422 — Invalid Parameters

All parameter problems share the code `validation_error`; `detail` says which.

| `detail`                                                            | Cause                                         |
| ------------------------------------------------------------------- | --------------------------------------------- |
| `Unknown filter 'x'`                                                | Not an allowed filter for this endpoint.      |
| `Unknown sort 'x'`                                                  | Not a sortable column for this endpoint.      |
| `filter must be an object, e.g. filter[key]=value`                  | Sent `?filter=x` instead of `?filter[key]=x`. |
| `sort must be a comma-separated string, e.g. sort=name,-created_at` | Sent `?sort[]=x` instead of `?sort=x`.        |
| `page[number] must be an integer` / `must be >= 1`                  | Non-numeric or zero/negative page.            |
| `page[size] must be an integer` / `must be between 1 and 100`       | Non-numeric or out-of-range page size.        |
| `filter[updated_since] must be a valid ISO 8601 datetime`           | Malformed timestamp.                          |
| `Missing required filter(s): filter[x]`                             | A report's required filter was omitted.       |
| `unsupported grant_type`                                            | `grant_type` was not `client_credentials`.    |

### 429 — Rate Limited

Slow down and honour `Retry-After`. See [Rate Limits & Policies](/services/mastery-connect/limits-policies.md).

## Handling Errors

A robust client distinguishes three classes:

**Retry immediately, once.** `401 invalid_token` — your token probably just expired. Refresh and replay. If the replay also fails, stop; something is wrong with the credential, and hammering it wastes your token budget.

**Retry later, with backoff.** `429` and `5xx`. Wait for `Retry-After` (or exponential backoff for `5xx`), then retry. Cap your attempts.

**Do not retry.** `403`, `404` and `422` are deterministic — the same request will always fail the same way. Log it and fix the request or the credential.

```python
def call(url, token):
    r = session.get(url, headers={"Authorization": f"Bearer {token}"})

    if r.status_code == 200:
        return r.json()

    code = r.json().get("errors", [{}])[0].get("code")

    if code == "invalid_token":
        token = refresh_token()          # once — refresh_token() must not loop
        return call_once(url, token)

    if r.status_code == 429:
        time.sleep(int(r.headers.get("Retry-After", 60)))
        return call(url, token)

    # 403 / 404 / 422 — deterministic, so surface it
    raise ApiError(r.status_code, code, r.json())
```

{% hint style="info" %}
`403 feature_disabled` is the one that most often looks like a bug in your code but isn't. It means the credential is fine and the API simply is not switched on for that district yet.
{% endhint %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://developerdocs.instructure.com/services/mastery-connect/errors.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
