neurolake audit · October 2026

How clean is OpenNeuro's metadata?

I checked the BIDS metadata of 150 randomly sampled OpenNeuro datasets against 22 rules, most taken from the BIDS specification and schema. About half pass without a single warning. Hard errors are uncommon. The weak spot is participant bookkeeping, and a handful of acquisition inconsistencies would quietly trip up a pooled analysis.

150datasets sampled
6,587subjects
57,382data files, 5.61 TB described
51%of raw datasets fully clean

Browse all 150 datasets, with every finding rule by rule.

Where the problems are

Of the 148 raw datasets, 76 (51%) have no warnings or errors, 68 have at least one warning, and 10 (7%) have an error that breaks the standard. Most findings are about consistency across files (participants against data folders, sidecars against each other), which a check of one file at a time can miss.

▲ error: breaks BIDS◆ warning: likely to cause trouble
◆No participants.tsv
18.9%
◆Recommended sidecar field missing
17.6%
◆Listed participant has no data
6.8%
◆Sampling rates differ within a task
4.1%
▲participant_id lacks sub- prefix
3.4%
◆.bidsignore matches core files
3.4%
◆TRs differ within a task
3.4%
◆Subject missing from participants.tsv
3.4%
▲Required sidecar field missing
2.7%
◆Key repeated in a JSON file
2.0%
▲Duplicate participant_id
0.7%
▲Malformed TSV row
0.7%
◆Two sidecars apply at one level
0.7%
◆.bidsignore hides all data
0.7%
◆scans.tsv lists a missing file
0.7%
0%5%10%15%20%
Share of the 148 raw datasets with at least one finding per rule. A dataset can appear under several rules. Informational findings are left out (they are in the table). Hover or focus a bar for counts.
All rules as a table
SeverityRuleDatasetsShareFindingsWhat it checks
errorparticipant_id lacks sub- prefix
participant_id_without_prefix
53.4%5participant_id values in participants.tsv lack the required sub- prefix
errorRequired sidecar field missing
missing_required_sidecar_field
42.7%1,130A field the BIDS schema marks REQUIRED for this datatype is not set by any applicable sidecar (skipped when a sidecar was not fetched)
errorDuplicate participant_id
duplicate_participant_id
10.7%1The same participant_id appears on more than one participants.tsv row
errorMalformed TSV row
malformed_tsv_row
10.7%1A TSV row has a different number of columns than its header
warningNo participants.tsv
missing_participants_tsv
2818.9%28The dataset has subjects but no participants.tsv
warningRecommended sidecar field missing
missing_recommended_sidecar_field
2617.6%781A field the BIDS schema marks RECOMMENDED for this datatype is not set by any applicable sidecar
warningListed participant has no data
participant_without_data
106.8%246A participants.tsv row has no data files in the snapshot
warningSampling rates differ within a task
inconsistent_sampling_frequency
64.1%9EEG/MEG/iEEG recordings of the same task and acquisition use SamplingFrequency values more than 0.5% apart
warning.bidsignore matches core files
bidsignore_matches_core_files
53.4%5A .bidsignore pattern also matches core files such as dataset_description.json (they were kept)
warningTRs differ within a task
inconsistent_repetition_time
53.4%9BOLD runs of the same task and acquisition use RepetitionTime values more than 0.5% apart
warningSubject missing from participants.tsv
subject_missing_from_participants
53.4%58A subject has data files but no row in participants.tsv
warningKey repeated in a JSON file
duplicate_json_key
32.0%677A JSON metadata file repeats a key. JSON leaves the result undefined and neurolake uses the last value as most readers do
warningTwo sidecars apply at one level
ambiguous_sidecar_inheritance
10.7%11More than one sidecar applies to a data file at the same folder level (BIDS 1.7.0 and later forbid this)
warning.bidsignore hides all data
bidsignore_hides_all_data
10.7%1.bidsignore covers every data file, so the dataset's data escapes all checks
warningscans.tsv lists a missing file
scans_tsv_missing_file
10.7%24A scans.tsv row references a file that does not exist
infoTwo sidecars apply at one level
ambiguous_sidecar_inheritance
21.4%28More than one sidecar applies to a data file at the same folder level (BIDS 1.7.0 and later forbid this)
infoMetadata not fetched (annexed)
metadata_not_fetched
10.7%421A metadata file is stored in git-annex and its content was not fetched during ingest

Participants are the weak spot

28 datasets (19%) have no participants.tsv at all. The file is only RECOMMENDED by BIDS, but without it age, sex and group are simply not available to anyone reusing the data. Where the file does exist, 10 datasets list people with no data in the release, 5 have data folders for subjects the table never mentions, and 5 write participant_id without the required sub- prefix.

Small formatting slips are enough to break a join. In ds004226 the two sides differ only in zero padding. In ds001838 the table also drops the prefix, so its unpadded IDs match nothing:

dataset
participants.tsv says
data folders are
ds001838
1, 2, 3
sub-01, sub-02, sub-04
ds004226
sub-1, sub-2, sub-3
sub-001, sub-002, sub-003

Hard errors are uncommon and concentrated

All errors come from 10 datasets. In 4 of them the only error is the missing sub- prefix above. The rest:

Each of these is cheap to fix and expensive to discover late, which is the case for checking metadata in a pipeline rather than at analysis time.

Same task, different acquisition

11 datasets mix acquisition parameters within a single task and acquisition label: repetition times for fMRI, or sampling rates for EEG, MEG and iEEG (differences under 0.5% are ignored as rounding). None of this breaks BIDS, and much of it is deliberate. Multi-site and clinical recordings vary by scanner or hospital. It still matters, because a pipeline that reads one TR or one sampling rate per task will be wrong for some runs.

The largest spread is in ds005003, where resting-state runs use 12 different TRs (2.16 to 3.53 s). In the iEEG dataset ds005522, recordings of a single task range from 500 to 2000 Hz.

.bidsignore files that ignore too much

A .bidsignore tells the validator which files to skip. In 5 datasets a pattern also matches core files such as the README or the dataset description. neurolake never ignores core files and reports the pattern instead.

Are newer datasets cleaner?

Datasets whose latest snapshot is from 2024 or later are clean 61% of the time (47 of 77), against 41% for older ones (29 of 71). Fisher's exact test gives p = 0.02, but treat that as suggestive. An earlier version of the rules gave p = 0.07 on the same sample, so the result depends on which rules count, and a snapshot's year is when the dataset was last updated, not first published. The share without participants.tsv falls too (p = 0.06).

0%25%50%75%100%
13%
33%
50%
35%
90%
68%
68%
43%
2019 and earliern=15
2020n=15
2021n=14
2022n=17
2023n=10
2024n=25
2025n=31
2026n=21
Share of raw datasets with no warnings or errors, by the year of their latest snapshot. Each bar is labelled with the number of datasets in that year.

Do datasets get cleaner after release?

The year comparison above uses each dataset's latest snapshot, which mixes when a dataset was published with how much it was revised. To separate the two I ran the same rules on the first published snapshot of every dataset in the sample. 88 of the 150 have been updated at least once, a median of 0.4 years apart, and all 88 are raw datasets.

Updates rarely touch the metadata. In 72 of the 88 updated datasets every finding is the same in the latest snapshot as in the first. The share that is fully clean moves from 53% to 58% (47 to 51), and the number with errors is 6 at both ends: 1 fixed, 1 introduced. Datasets that ended with fewer findings outnumber those that ended with more, 9 to 5, which is well within chance (sign test p = 0.42).

First snapshotcleanwarnings onlyerrors
clean4700
warnings only4301
errors015
Status of the 88 updated datasets at their first snapshot (rows) and latest snapshot (columns). The diagonal is datasets whose status did not change.

Counting individual findings tells a different story: 1,344 resolved against 46 introduced. But 1,275 of those come from one dataset, ds003768: 255 recordings that each lacked 5 required sidecar fields in its first snapshot (1.0.0) and have them all by 1.0.13. Some improvements are not metadata fixes. In 4 of the 16 datasets whose findings changed, the latest snapshot has at least half again as many data files as the first, which points to an unfinished first upload, and in 2 of them the gain is listed participants that simply received their data.

Updates also break things. By 1.0.4, ds004103 has a .bidsignore that covers every data file, so none of its recordings are checked any more. Its first snapshot (1.0.0) had no such pattern. ds001619 has fewer data files by 1.0.1, and 7 people still listed in its participants.tsv no longer have any data.

The practical reading: problems present at first upload tend to stay, so the cheapest place to catch them is before the first snapshot is published.

Cross-check with the official validator

To see where these rules stand against the reference tool, I ran the official bids-validator on the same 150 snapshots. It is the legacy version 1.15.0 from npm, not the current schema-based 2.x, which is distributed through Deno and could not be installed here. Because imaging data is never downloaded, it ran in git-tree mode on the shallow clones (--gitTreeMode) with NIfTI header checks switched off (--ignoreNiftiHeaders), so its header checks are excluded from everything below.

The validator could assess 144 of the 150 datasets. It stopped early on the rest (INCOMPLETE_DATASET in 4, QUICK_VALIDATION_FAILED in 1, crashed without JSON output in 1). Agreement is computed over those 144 only: a dataset the validator could not assess never counts as agreement.

11 neurolake rules have a validator check that tests the same thing (audit/validator_map.csv). On those, the two tools flagged 25 dataset-check pairs between them and agreed on 8 (32%). Leaving out the participants.tsv vs subject folders check, where validator 1.15.0 never emits PARTICIPANT_ID_MISMATCH, they agree on 7 of 13 (54%). Each disagreement is classified by rules in audit/validator.py, and 5 of the 17 disagreements were checked by hand.

Check (validator code)BothOnly neurolakeOnly validatorNeitherNot assessed
participants.tsv vs subject folders
NO_VALID_DATA_FOUND_FOR_SUBJECT, PARTICIPANT_ID_MISMATCH
11101321
participant_id lacks sub- prefix
PARTICIPANT_ID_PATTERN
5001390
Required sidecar field missing
JSON_SCHEMA_VALIDATION_ERROR/sidecar_required, REPETITION_TIME_MUST_DEFINE, TASK_NAME_MUST_DEFINE
0401400
TSV row length differs from header
TSV_EQUAL_ROWS
1011420
No dataset_description.json
DATASET_DESCRIPTION_JSON_MISSING
0011430
scans.tsv lists a missing file
SCANS_FILENAME_NOT_MATCH_DATASET
1001430
Invalid JSON
JSON_INVALID
0001440
IntendedFor target missing
INTENDED_FOR
0001440
Description lacks Name or BIDSVersion
JSON_SCHEMA_VALIDATION_ERROR/dataset_description_required
0001440
TR that looks like milliseconds
REPETITION_TIME_GREATER_THAN
0001440
Unreadable file
FILE_READ
0001440
Datasets per paired check, over the 144 the validator assessed. The last column counts datasets the validator could not assess where neurolake has a finding; they are left out of the other columns.

15 disagreements in 13 datasets are problems neurolake reports and validator 1.15.0 does not. 11 datasets on participants.tsv vs subject folders covering 215 participant IDs (some people appear twice, once per ID spelling). 4 datasets on required sidecar field missing covering 226 recordings. The recordings have no sidecar at all: validator 1.15.0 checks EEG, iEEG and MEG sidecar files against its schema but does not require that such a recording has one.

The participants check is worth a note. A synthetic dataset whose participants.tsv and subject folders disagree passes validator 1.15.0 without PARTICIPANT_ID_MISMATCH (run in normal and git-tree mode), while the same file's missing sub- prefix is reported, so the table was read. The check is unreachable in 1.15.0: fullTest declares const participants = null (dist/commonjs/cli.js:137602, dist/commonjs/index.js:133003) and passes it to the participants check. Its silence on these datasets says nothing about them.

The other way, 1 disagreement is a problem the validator reports and neurolake misses. ds002841: neurolake does not parse task-masstask_run-01_events.tsv.

.bidsignore semantics (1). ds006833: .bidsignore matches dataset_description.json: the validator ignores it, neurolake never ignores core files.

None of the 17 disagreements comes from the two tools following different versions of the specification.

Every disagreement and its class
ClassOnly neurolakeOnly validatorValidator could not assess
.bidsignore semantics010
validator could not assess001
real neurolake miss010
real validator miss1500
DatasetCheckDirectionClassWhy
ds001619participants.tsv vs subject foldersOnly neurolakereal validator missvalidator 1.15.0 never reports PARTICIPANT_ID_MISMATCH: a synthetic dataset with that defect passed (audit/validator_probes.json)
ds001838participants.tsv vs subject foldersOnly neurolakereal validator missvalidator 1.15.0 never reports PARTICIPANT_ID_MISMATCH: a synthetic dataset with that defect passed (audit/validator_probes.json)
ds002338participants.tsv vs subject foldersOnly neurolakereal validator missvalidator 1.15.0 never reports PARTICIPANT_ID_MISMATCH: a synthetic dataset with that defect passed (audit/validator_probes.json)
ds004040participants.tsv vs subject foldersOnly neurolakereal validator missvalidator 1.15.0 never reports PARTICIPANT_ID_MISMATCH: a synthetic dataset with that defect passed (audit/validator_probes.json)
ds004166participants.tsv vs subject foldersOnly neurolakereal validator missvalidator 1.15.0 never reports PARTICIPANT_ID_MISMATCH: a synthetic dataset with that defect passed (audit/validator_probes.json)
ds004226participants.tsv vs subject foldersOnly neurolakereal validator missvalidator 1.15.0 never reports PARTICIPANT_ID_MISMATCH: a synthetic dataset with that defect passed (audit/validator_probes.json)
ds005059participants.tsv vs subject foldersOnly neurolakereal validator missvalidator 1.15.0 never reports PARTICIPANT_ID_MISMATCH: a synthetic dataset with that defect passed (audit/validator_probes.json)
ds005366participants.tsv vs subject foldersOnly neurolakereal validator missvalidator 1.15.0 never reports PARTICIPANT_ID_MISMATCH: a synthetic dataset with that defect passed (audit/validator_probes.json)
ds005522participants.tsv vs subject foldersValidator could not assessvalidator could not assessvalidator could not assess the dataset (INCOMPLETE_DATASET)
ds005697participants.tsv vs subject foldersOnly neurolakereal validator missvalidator 1.15.0 never reports PARTICIPANT_ID_MISMATCH: a synthetic dataset with that defect passed (audit/validator_probes.json)
ds006833participants.tsv vs subject foldersOnly neurolakereal validator missvalidator 1.15.0 never reports PARTICIPANT_ID_MISMATCH: a synthetic dataset with that defect passed (audit/validator_probes.json)
ds007427participants.tsv vs subject foldersOnly neurolakereal validator missvalidator 1.15.0 never reports PARTICIPANT_ID_MISMATCH: a synthetic dataset with that defect passed (audit/validator_probes.json)
ds002841TSV row length differs from headerOnly validatorreal neurolake missneurolake does not parse task-masstask_run-01_events.tsv
ds006833No dataset_description.jsonOnly validator.bidsignore semantics.bidsignore matches dataset_description.json: the validator ignores it, neurolake never ignores core files
ds004166Required sidecar field missingOnly neurolakereal validator missthe recordings have no sidecar at all: validator 1.15.0 checks EEG, iEEG and MEG sidecar files against its schema but does not require that such a recording has one
ds004348Required sidecar field missingOnly neurolakereal validator missthe recordings have no sidecar at all: validator 1.15.0 checks EEG, iEEG and MEG sidecar files against its schema but does not require that such a recording has one
ds005065Required sidecar field missingOnly neurolakereal validator missthe recordings have no sidecar at all: validator 1.15.0 checks EEG, iEEG and MEG sidecar files against its schema but does not require that such a recording has one
ds005697Required sidecar field missingOnly neurolakereal validator missthe recordings have no sidecar at all: validator 1.15.0 checks EEG, iEEG and MEG sidecar files against its schema but does not require that such a recording has one
Checked by hand against the raw files (6)

3 of the 15 neurolake-only and 2 of the 2 validator-only disagreements and 1 agreement were opened in the cloned snapshots.

  • ds004226 (participants.tsv vs subject folders): participants.tsv lists sub-1, sub-2, sub-3... while the folders are sub-001, sub-002, sub-003...: the two sides never match.
  • ds006318 (participants.tsv vs subject folders): All 86 participants.tsv rows match the 86 folders, and every folder holds only JPEG images under derivatives/, no BIDS data file. The validator reports NO_VALID_DATA_FOUND_FOR_SUBJECT for all 86.
  • ds002338 (participants.tsv vs subject folders): participants.tsv lists sub-xp208, sub-xp209, sub-xp212, sub-xp214 and none of them has a folder.
  • ds004348 (Required sidecar field missing): All nine subjects have a task-wake_eeg.set with no eeg.json at any level (only task-sleep has one), so the five REQUIRED EEG fields are undefined: 45 findings, 9 recordings x 5 fields.
  • ds002841 (TSV row length differs from header): Row 36 of the root task-masstask_run-01_events.tsv ends with a tab, giving 4 columns under a 3-column header. neurolake does not parse events files.
  • ds006833 (No dataset_description.json): dataset_description.json exists with Name and BIDSVersion. The .bidsignore line **/*dat* matches it, which validator 1.15 honours.

Outside the paired checks the two tools look at different things. 9 neurolake rules with findings here have no validator counterpart: No participants.tsv (28 datasets), Recommended sidecar field missing (26 datasets), Sampling rates differ within a task (6 datasets), .bidsignore matches core files (5 datasets), TRs differ within a task (5 datasets), Key repeated in a JSON file (3 datasets), Two sidecars apply at one level (1 dataset), .bidsignore hides all data (1 dataset), Duplicate participant_id (1 dataset). In the other direction, the validator raised 11 kinds of error with no neurolake rule, most often JSON_SCHEMA_VALIDATION_ERROR other than a missing required field (16 datasets), NOT_INCLUDED (10 datasets), TSV_EMPTY_CELL (6 datasets), MULTIPLE_COMMAS_IN_AUTHOR_FIELD (3 datasets). neurolake has no rule for any of these.

How this was done

Limits

This covers 150 of roughly 1,500 to 2,000 mirrored datasets, at their first and latest snapshots only, not every version in between. The rules cover the metadata problems that most often break reuse, not the whole specification, so this complements the official validator rather than replacing it. Metadata stored in git-annex was not fetched, and the affected files are marked unknown rather than missing.

Reproduce it

pip install -e .
neurolake discover-mirrors --sample 150 --max-id 7500 --seed 2026 \
    --log-file audit/probe_log.csv > audit/sample_ids.txt
neurolake ingest-mirror --from-file audit/sample_ids.txt --lake data/audit
neurolake transform --lake data/audit
python audit/analyze.py data/audit
python audit/render.py
make history   # first vs latest snapshots
make validator-run validator   # cross-check with bids-validator

Code, rules and tests: github.com/pearlfranz20/neurolake.