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

# Errors & Validation

Error response format, validation rules, and accepted date formats

All endpoints share a single error contract and a single request-validation layer, so the error you get back is consistent across the API.

## Error Response Format

Every error response is JSON with an `error` code and a human-readable `message`:

```json
{
  "error": "missing_external_id",
  "message": "external_id is required"
}
```

Some errors include extra context — e.g. file-size errors add `max_size_mb`:

```json
{
  "error": "file_too_large",
  "message": "File size exceeds 40MB limit",
  "max_size_mb": 40
}
```

* `error` (string) — stable machine-readable code. Branch on this, not on the HTTP status alone.
* `message` (string) — human-readable description; wording may change, the code will not.

## Request Validation

JSON request bodies are validated against the same schemas that define the API. Two failure modes:

| Situation                                      | Status | `error`                         |
| ---------------------------------------------- | ------ | ------------------------------- |
| Body is not valid JSON                         | `400`  | `invalid_json`                  |
| Body is valid JSON but fails schema validation | `422`  | field-specific code (see below) |

When schema validation fails, the `error` code is resolved from the **first failing field** (so a missing `external_id` returns `missing_external_id`, not a generic code), and the `message` is prefixed with the field path:

```json
{
  "error": "missing_external_id",
  "message": "external_id: Required"
}
```

## Common Error Codes

| Status | `error`                    | Meaning                                                            |
| ------ | -------------------------- | ------------------------------------------------------------------ |
| `400`  | `invalid_json`             | Request body is not valid JSON                                     |
| `401`  | `invalid_api_key`          | Missing or invalid `Authorization` bearer token                    |
| `404`  | `patient_not_found`        | Patient does not exist in your organization                        |
| `404`  | `test_not_found`           | Test ID does not exist                                             |
| `409`  | `no_prior_test`            | Patient has no completed test to anchor interpretation on          |
| `413`  | `file_too_large`           | File exceeds the 40 MB limit (includes `max_size_mb`)              |
| `415`  | `unsupported_content_type` | `Content-Type` is not accepted for this endpoint                   |
| `422`  | `missing_external_id`      | `external_id` is required                                          |
| `422`  | `missing_patient_id`       | `patient_id` is required                                           |
| `422`  | `missing_file`             | No file was provided in a multipart request                        |
| `422`  | `missing_data`             | The `data` form field is required                                  |
| `422`  | `missing_file_or_payload`  | A JSON `/process` request needs `file_url` or `payload.parameters` |
| `422`  | `invalid_gender`           | `gender` is not one of `male`, `female`, `other`, `unknown`        |
| `422`  | `duplicate_file_previous`  | The same file was already uploaded for this patient                |
| `422`  | `duplicate_file_in_batch`  | The same file appears more than once in one request                |
| `500`  | `internal_server_error`    | Unexpected server error — safe to retry                            |

## What Is *Not* Rejected

To keep ingestion robust, `/process` and `/upload` accept best-effort content rather than failing the whole request on a single bad field:

* **Missing / `unknown` gender** — accepted. Only an explicitly *invalid* gender value is rejected (`invalid_gender`).
* **Implausible or missing `weight` / `height`** — accepted as-is.
* **Unparseable `birth_date` / `test_date`** — stored as empty rather than rejected.
* **Qualitative parameter values** — a parameter `value` may be a number, a string (e.g. `"Negative"`, `"Clear"`), or `null`; `unit` may be an empty string for unitless observations.

Gaps like these surface as data-quality review reasons (for organizations that enable review), not as request errors.

## Date Formats

Date fields (`birth_date`, `test_date`, …) accept a range of formats — you do **not** have to send strict `YYYY-MM-DD`:

| Family             | Examples                                 |
| ------------------ | ---------------------------------------- |
| Year-first (YMD)   | `2024-12-31`, `2024/12/31`, `2024.12.31` |
| Day-first (DMY)    | `31-12-2024`, `31/12/2024`, `31.12.2024` |
| Month-first (MDY)  | `12-31-2024`, `12/31/2024`               |
| ISO 8601 timestamp | `2024-12-31T10:30:00Z`                   |

Notes:

* Separators `-`, `/`, and `.` are all accepted.
* Two-digit years are interpreted as 2000s (e.g. `24` → `2024`).
* Ambiguous dates (e.g. `01-02-2024`) are resolved using your organization's configured date format; if none is set, BloodGPT defaults to **day-first (DMY)**. To avoid ambiguity, prefer year-first `YYYY-MM-DD`.
