GR00T rejects my dataset: LeRobot v3.0 is not supported

A GR00T run that fails in under two minutes with no loss values almost always failed on the dataset version, not on your data. One field in meta/info.json decides it.

Dataset · Last updated 2026-08-09

Symptom

The GR00T training job reaches RUNNING, then ends as FAILED within a minute or two. The error text mentions dataset metadata, and the run page never shows a single loss value.

  • GR00T N1.7
  • GR00T N1.5
Short answer

The GR00T trainer accepts LeRobot v2.1 datasets and rejects v3.0 at load time, which is why the job dies before step one. Open meta/info.json and read codebase_version. If it says v3.0, convert the tree to v2.1 and upload the converted copy, or train a lerobot-family model on it instead.

What the rejection looks like from the outside

Nothing about the first minute looks wrong. The job takes a slot, a container comes up, the status turns RUNNING, and then it lands on FAILED before the first metric is written. The run page shows an empty chart because there was never a step to plot. The message talks about dataset metadata rather than naming a version, so the natural reading is that the upload broke halfway, and the natural response is to upload the same directory again. That reproduces the failure exactly.

The upload is fine. The GR00T trainer reads the dataset metadata, finds a format it does not load, and stops. Parsing two lines of JSON is cheap, which is precisely why the failure arrives so fast. A slow failure would mean something else went wrong.

The one field that decides

A LeRobot dataset carries its format version in meta/info.json, at the top of the file. GR00T loads v2.1. It does not load v3.0. This applies to both GR00T N1.7 at /policies/groot-n1-7 and GR00T N1.5 at /policies/groot-n1-5, since they share the same data path.

json
{
  "codebase_version": "v3.0",
  "robot_type": "so100",
  "fps": 30,
  "total_episodes": 58,
  "features": {
    "observation.state":         { "dtype": "float32", "shape": [6] },
    "action":                    { "dtype": "float32", "shape": [6] },
    "observation.images.scene":  { "dtype": "video",   "shape": [480, 640, 3] },
    "observation.images.wrist":  { "dtype": "video",   "shape": [480, 640, 3] }
  }
}
A dataset GR00T will refuse. Everything below the first line is fine.

Note what is not wrong here. The robot type is right, the frame rate is right, both camera streams are present with sensible shapes, and 58 episodes clears the 50 that GR00T needs before a result is worth judging. None of that gets read, because the version check comes first.

bash
# Five seconds, before you upload anything
grep codebase_version meta/info.json
#   "codebase_version": "v3.0",

# While you are in there, confirm the episode count matches what you recorded
grep total_episodes meta/info.json
#   "total_episodes": 58,

Why this usually means the data came from somewhere else

The desktop client writes v2.1. That is the default and it is the format the platform records, uploads and trains on end to end. So if every episode in the set came out of ay-robots record on your own machine, a v3.0 tree is close to impossible. The version field is a clue about origin, and in practice it points at an import.

Where the dataset came fromVersion you getWhat to do about it
Recorded with the desktop client or ay-robots recordv2.1Nothing. This is the tested path, described at /learn/record-your-first-dataset.
Pulled from the Hugging Face HubWhatever the author published, and v3.0 is increasingly commonRead the field before you queue an 80 GB card.
Recorded with your own upstream lerobot installWhatever that installed version writesRead the field. Do not infer it from the lerobot version you think you have.
Merged, filtered or converted by a script of your ownWhatever your script wrote into the fileRead the field, then check total_episodes as well, since merges lose episodes quietly.
v3.0 is not a quality problem

The two formats hold the same demonstrations. Nothing about your recordings is worse because the directory says v3.0. This is a loader compatibility issue and it says nothing at all about whether the data will train a good policy.

Confirm it in four steps

  1. 1
    Read the version field

    Open meta/info.json in the dataset directory, or grep it. If it reads v2.1 the version is not your problem and you should look at the failure message again, because something else stopped the run.

    bash
    grep codebase_version meta/info.json
  2. 2
    Confirm the trainer refused it, not the upload

    A dataset that never finished processing cannot be trained on either, and that looks similar from the dashboard. Pull the job record and read status together with errorMessage. A job that reached RUNNING was handed a real container, which means the upload arrived.

    bash
    curl -X POST https://www.ay-robots.com/api/mcp \
      -H "Authorization: Bearer $AY_ROBOTS_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "jsonrpc": "2.0",
        "id": 1,
        "method": "tools/call",
        "params": {
          "name": "get_training_job",
          "arguments": { "job_id": "job_7ad402" }
        }
      }'
  3. 3
    Check whether you still have the local original

    If you recorded the episodes yourself, the local copy is the v2.1 source of truth and the cloud copy is the import that went sideways. List what is on the machine before you plan a conversion you may not need.

    bash
    ay-robots datasets list --local
  4. 4
    Pick a route and only then re-upload

    Upload the converted copy under a new name rather than replacing the original. Two datasets that differ only in format are worth the storage, because the next time a run fails you want to be able to tell which one you trained on.

    bash
    ay-robots datasets upload cube-pick-v21 --name "SO-100 cube pick v2.1"

The three routes out

RouteWhat it costs youWhen it is the right one
Convert the v3.0 tree to v2.1 with the lerobot conversion toolingMinutes of CPU time and a second copy on diskYou have the full tree locally and want to keep the exact episodes you have.
Re-record with the clientHours, since 50 episodes is the floor for GR00TThe imported set was never yours, or you never trusted its recording conditions anyway.
Train a lerobot-family model on the data as it standsA different model, so a different resultYou want an answer today and the question is whether the data is any good.

The third route needs a caveat, because it is easy to over-read. The hard rejection lives in the GR00T trainer. ACT, SmolVLA and Pi0.5 load through the lerobot dataset layer, which is the code that introduced v3.0 in the first place, so that path is the tolerant one. It is a fallback and not a guarantee: v2.1 is the format this platform records and the format documented for all five models in the catalog. A v2.1 copy is the answer that always works. If you take the fallback, /train/smolvla-on-so-100 is the cheapest way to find out whether the episodes are worth converting at all, and /compare/groot-n1-7-vs-smolvla covers what you give up.

Check the version before the upload, not after the queue

GR00T asks for the 80 GB GPU tier, which is the tier with the fewest offers and the longest waits. Discovering a version mismatch after you have pushed several gigabytes and waited for a card is the expensive ordering of the same two actions.

After the conversion

Re-read meta/info.json on the converted copy before you queue anything. A conversion that silently dropped episodes is the second failure in this family, and it does not announce itself: the run starts, the loss falls, and you spend an evening wondering why a policy trained on what you believe were 58 episodes behaves like one trained on 30. Compare total_episodes against the number you recorded, and confirm both camera keys survived under features.

Frequently asked questions

Can I just rename the version string to v2.1?

No. The field describes a directory layout, not a preference. Editing the string gets you past the version check and into a loader that then reads files which are not where it expects them, which turns a clear early failure into a confusing later one. Convert the tree properly.

Does GR00T N1.5 accept v3.0 even though N1.7 does not?

No. Both GR00T entry points take LeRobot v2.1 only. They share the dataset format and the workflow, which is one of the few things that has not changed between them.

Will the client ever write v3.0?

It writes v2.1 today, and it does so deliberately, because v2.1 is the format every supported model can load. If that changes, the client is the piece that changes, not your existing datasets.

My run failed fast and codebase_version says v2.1. What now?

Then the version is not the cause. The other fast failure is a dataset that never finished processing, so the trainer had nothing to open. Check the dataset status in the dashboard or through get_dataset on the MCP server, and confirm the episode count is what you expect.

Is it worth converting a dataset someone else published?

Only if you know how it was recorded. A dataset from another arm, another camera placement and another operator will train a policy that is not about your setup. It is useful for checking that your training pipeline runs, and much less useful as the data behind a policy you plan to deploy.