> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nomadicml.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Validation

> What is rejected or warned, and how to check an overlay before you upload.

A file is checked in two stages:

1. **The overlay**, on its own: fields, types, cross-references between parts and signals. This
   runs in [preflight](#preflight), before you upload anything, and again at ingest.
2. **The data**, at ingest: that declared channels exist and carry the right messages, video
   encodings, point-cloud frames, rates and heights.

Every finding names a **rule id** (e.g. `segment.end_s.invalid`), a **location** (e.g.
`recording.platform.parts[2] left_hand`) and a message saying what to change.

## Rejected and warned

| Rejected: the file is not ingested | Warned: the file is ingested |
| - | - |
| No `nomadic_spec` metadata record, or its `spec` value is not a JSON object | `primary_view` missing while views are declared |
| The overlay fails validation (any overlay rule on these pages) | A view role that is not canonical |
| A declared channel is absent from the file | A `vector` signal without `components` |
| A declared view's channel is not decodable media | An observed signal rate more than 20% off its `rate_hz` |
| A part's `end_pose` / `keypoints` channel does not carry protobuf `foxglove.PoseInFrame` / `nomadic.Skeleton` | An observed value outside a declared `enum` |
| A sensor-frame point cloud with no transform to `vehicle` | LiDAR height distribution inconsistent with the sensor's mounting height |
| h265 video with B-frames | `t0_ns` that is not absolute epoch nanoseconds |
| An unsupported media encoding | `mount` in a 1.1 overlay (declare `kind`) |
| No video producible: no camera **and** no LiDAR with `ring` | A part with no `joints`, `end_pose` or `keypoints` |
| | A hand with fewer than 5 keypoints, or a sided hand whose name lacks its side |
| | 1.0 arm signal names or the bimanual profile next to declared parts |
| | More than one `LocationFix` channel, or fewer than two valid fixes |
| | Keypoint messages with the wrong joint count, or poses with an invalid quaternion (those messages are skipped) |

## Preflight

Check an overlay **before** you upload: preflight sends only the overlay JSON (kilobytes), never
the recording.

```bash theme={null}
curl -X POST https://api-prod.nomadicml.com/api/mcap/spec/preflight \
  -H "X-API-Key: $NOMADIC_API_KEY" \
  -H "Content-Type: application/json" \
  --data @overlay.json
```

```python theme={null}
import json, os, requests

overlay = json.load(open("overlay.json"))
report = requests.post(
    "https://api-prod.nomadicml.com/api/mcap/spec/preflight",
    headers={"X-API-Key": os.environ["NOMADIC_API_KEY"]},
    json=overlay,
    timeout=30,
).json()

if not report["ok"]:
    for finding in report["errors"]:
        print(finding["rule"], finding["location"], finding["message"])
```

The response is always `200` for a well-formed request; a bad overlay is reported in the body (abridged):

```json theme={null}
{
  "ok": false,
  "spec_version": "1.1",
  "errors": [
    {"severity": "error", "rule": "part.joints.names.length",
     "location": "recording.platform.parts[0] left_arm.joints",
     "message": "names lists 6 joints but the signal has dim 7"}
  ],
  "warnings": [
    {"severity": "warning", "rule": "signal.components.missing",
     "location": "signals[3] /sig/left_arm_joints",
     "message": "no components declared, so axes will be labelled by index"}
  ]
}
```

`ok` is `true` when there are no errors. Warnings never block ingest.

<Note>
  Preflight checks the overlay only. Checks that need the messages (channel presence, encodings,
  LiDAR heights, observed rates, GPS fixes) run at ingest and are reported on the ingest.
</Note>

## Schema-only validation

You can also validate the overlay offline against the [JSON Schema](/mcap-spec/schemas#overlay-json-schema)
with any JSON Schema 2020-12 validator. The schema covers field shapes; preflight additionally
checks the rules a schema cannot express, such as `end_s` after `start_s`, a part's signal being
declared with the right type and unit, joint-name counts and keypoint trees. Treat preflight as
authoritative.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.