# Errors, responses, and retries

Read the actual response shape, distinguish accepted work from completion, and retry only when safe.

Source: https://docs.minds.sh/docs/api/platform/errors



Always inspect the HTTP status before reading a result. The platform has several response styles because its browser, management, and instance surfaces serve different clients.

## Success is not universally wrapped [#success-is-not-universally-wrapped]

The management helper `apiSuccess(data)` returns **data directly**. It does not automatically add `{data:...}`. A service's own response type may contain `data`, `tenants`, or another collection field. Instance listing returns a raw array. Dashboard provisioning returns `{instance}`. Proxies preserve the upstream body.

| Status | Usual meaning                                                                           |
| ------ | --------------------------------------------------------------------------------------- |
| 200    | A read or synchronous operation returned a result                                       |
| 201    | A resource was created by this route                                                    |
| 202    | An asynchronous operation was accepted, or a dashboard readiness check is still pending |
| 204    | Success with no response body                                                           |

A 202 creation, backup, or restore response does not mean that the provider operation has finished.

## Management errors [#management-errors]

Routes using `apiError()` return this shape:

```json
{
  "error": {
    "code": "INVALID_REQUEST",
    "message": "Invalid request body",
    "details": { "validation_errors": [] },
    "request_id": "<request-id>"
  }
}
```

| Status | Common code                             | Action                                                             |
| ------ | --------------------------------------- | ------------------------------------------------------------------ |
| 400    | `INVALID_REQUEST`                       | Correct the request fields, types, or referenced resources         |
| 401    | `AUTHENTICATION_REQUIRED`               | Supply a valid credential for this API surface                     |
| 403    | `PERMISSION_DENIED`                     | Check organization membership and credential scope                 |
| 404    | `RESOURCE_NOT_FOUND`                    | Verify the resource ID in the authorized scope                     |
| 409    | `CONFLICT`                              | Reload current state before deciding whether to retry              |
| 429    | `RATE_LIMIT_EXCEEDED`                   | Honor `Retry-After` when present                                   |
| 500    | `INTERNAL_ERROR`, `PROVISIONING_FAILED` | Preserve the request ID and inspect resource/provider state        |
| 503    | `SERVICE_UNAVAILABLE`                   | Retry a safe read after backoff or wait for the service to recover |

Some route-specific codes extend this set. Dashboard routes also use `{error:string}` and `{error:{code,message}}`; do not assume all bodies validate against the management error schema.

## Validation [#validation]

The management service parses request bodies and queries with the schemas in [Schema reference](/docs/api/platform/schemas). Malformed JSON and invalid fields produce 400. Optional fields and defaults are explicit in the schemas. Query parameters arrive as strings, so coercion is available only where the schema defines it.

## Request and rate-limit metadata [#request-and-rate-limit-metadata]

Management routes that call `withRequestMetadata()` add `X-Request-ID`. Their rate-limit results may also add `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, and `Retry-After`. Keep the request ID with your own diagnostic event; never include the credential.

## Retry behavior [#retry-behavior]

Retry read-only requests with bounded backoff. For a timeout after a create, scale, delete, restore, or billing operation, inspect the resource before repeating the mutation. The first request may have reached the provider even when the client received no success response.

Tenant and service creation read `Idempotency-Key`. The current helper stores a response under the supplied key for 24 hours, but the source does not establish request-body binding or a transaction spanning provider work. Use a unique key per logical operation and do not describe this implementation as exactly-once processing.

## Instance tool errors [#instance-tool-errors]

The dedicated daemon's MCP HTTP tool endpoint can return an HTTP-success response with `is_error:true` in its tool envelope. Check both levels:

```typescript
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const output = await response.json();
if (output.is_error) throw new Error('The memory tool rejected the operation');
```

Source: `minds-ui/apps/api-service/types/errors.ts`, `lib/api-response.ts`, `lib/validation.ts`, `lib/idempotency.ts`, and `akasha/mcp/src/tools/mod.rs`.
