Every validation issue carries a stable code (for example IDENT001).
Codes are permanent: once shipped, a code is never renumbered or reused for
a different meaning, even if the underlying check is later refined. If a
check is removed, its code is retired, not recycled. (Two codes in this
catalogue — MATRIX003 and FEAT002 — were sketched with a different
meaning in an early design draft but never shipped that way, so they were
free to be assigned their current, shipped meaning; see the note at the
bottom of this page.)
Each row lists the default severity (error, warning, or information),
the check module that emits it, and the exact remediation text a user sees
in the issue's remediation field. Every check reports location using
the physical AnnData column it resolved (for example obs.Metadata_Plate)
wherever a column was actually resolved; when nothing resolved, location
names the canonical field instead (for example obs.plate).
Fallback code: no column resolved for a required canonical identifier field that has no dedicated rule code of its own. Only reachable via a custom schema that declares a required field beyond the five below (plate, well, site, cell_id, perturbation_id); the built-in schemas never emit it. The issue's location (obs.<field>) names the field.
Add an .obs column for the field named in location using one of the schema's declared aliases, or drop it from that profile level's required_for list in your custom schema.
IDENT001
error
No column resolved for the canonical plate field, required for the effective profile level.
Add an .obs column for plate using one of the schema's declared aliases, or choose a schema/profile level that matches this dataset.
IDENT002
error
No column resolved for the canonical well field.
Same as IDENT001, for well.
IDENT003
error
No column resolved for the canonical site field (single-cell).
Same as IDENT001, for site.
IDENT004
error
No column resolved for the canonical cell_id field (single-cell only — not required at well or treatment level).
Same as IDENT001, for cell_id.
IDENT005
error
No column resolved for the canonical perturbation_id field (treatment).
Same as IDENT001, for perturbation_id.
IDENT006
error
A treatment-level profile has neither direct plate+well identifiers nor adequate uns['aggregation'] provenance (both method and source_level), so its rows cannot be traced back to source data.
Either keep plate/well identifier columns on treatment-level rows, or declare uns['aggregation'] with both 'method' and 'source_level'.
IDENT007
warning
A perturbation identifier resolved, but no perturbation modality column did. Do not use LINCS Metadata_pert_type for this — that field is control/treatment status (control_type).
Add a perturbation modality column (for example Metadata_perturbation_modality) with a value such as compound/orf/crispr.
IDENT008
warning
The perturbation modality column contains an unrecognized value.
Use a recognized modality (compound, orf, crispr, crispr_ko, crispr_a, unknown), or extend the schema/documentation to cover this value.
OBS001
error
Two or more observations share an identical identifier-column tuple.
Ensure each observation's identifier columns uniquely identify it, or remove/merge duplicated rows.
OBS002
error
An identifier column has one or more missing (null) values.
Populate the column for every observation, or remove incomplete rows.
Store feature values as a numeric dtype (integer or floating point).
MATRIX002
warning
.X contains NaN/Inf values (checked via bounded, sparse-safe sampling — never .toarray()).
Investigate and either impute, mask, or document non-finite values.
MATRIX003
error
.X's shape does not equal (n_obs, n_vars). A real anndata.AnnData enforces this on construction, so this only fires for a hand-edited or corrupted .h5ad file.
Ensure X has exactly one row per observation and one column per feature.
MATRIX004
error
A .layers entry's shape does not match .X's shape.
Ensure every layer has the same shape as X.
SLOT001
warning
No processing stage is declared for .X (uns['processing_stage']).
Set uns['processing_stage'] (for example 'raw', 'normalized', or 'aggregated').
SLOT002
error
An .obsm/.varm entry's first dimension does not match n_obs/n_vars.
Ensure every obsm/varm entry's first dimension matches n_obs/n_vars.
SLOT003
warning
One or more .layers entries have no declared processing stage.
Declare each layer's processing stage in uns['layer_processing_stages'][<layer name>].
Add a control/treatment annotation column (for example Metadata_pert_type) with values such as negcon/poscon/trt.
CTRL002
warning
The control/treatment column contains an unrecognized label.
Use one of the recognized labels (negcon, poscon, trt, control, treatment), a JUMP-style prefix (negcon_..., poscon_...), or extend the schema/documentation to cover this label.
CTRL003
warning
No negative control (negcon or negcon_...) annotation was found.
Include at least one negative control (for example labeled 'negcon' or 'negcon_...') to support downstream normalization and QC.
One or more feature names do not start with a schema-declared compartment prefix.
Prefix feature names with a declared compartment (for example 'Cells_'), or add the compartment to the schema.
FEAT002
warning
One or more feature names (that did match a compartment) don't encode a recognized measurement family (for example AreaShape, Intensity, Texture) right after their compartment prefix.
Name features as '<compartment>_<measurement family>_...' using one of the schema's declared measurement families, or add the family to the schema.
Add a batch identifier column if this dataset spans multiple experimental batches (aliases include batch_id, Metadata_Batch, and LINCS-style Metadata_Batch_Number).
META002
warning
No experiment metadata block (uns['experiment']).
Record experiment-level metadata (for example instrument, protocol, date) in uns['experiment'].
META003
information
No data-generating source/site identifier column resolved.
Add a source identifier column if this dataset spans multiple data-generating sites or laboratories.
A registered check raised an unexpected exception. The rest of the run continues; this issue replaces that check's (missing) result. Never emitted by an individual check itself.
Report this as a bug in cp-anndata-validator, including the check name and a minimal reproducing dataset if possible.
Physical column reporting. Whenever a check resolves an actual
.obs/.var column for a canonical field, its location (and often its
evidence) names that physical column, not the canonical name — see
CTRL002/CTRL003, OBS001/OBS002, IDENT008. Only when nothing
resolved does an issue fall back to the canonical field name (IDENT001
through IDENT005, IDENT007, META001, META003, CTRL001).
Bounded evidence, never raw sensitive dumps. Any check that could
otherwise list an unbounded number of values (duplicate identifiers,
unrecognized labels, unmatched feature names, undeclared layers) truncates
evidence to at most 5 examples and never dumps a full per-row value
table. Report/console/JSON/HTML renderers never re-expand these lists.
Profile-level-dependent requirements.IDENT001–IDENT005 only fire
for fields required by the effective profile level (declared, or
detected when not declared) — see Profile levels.
In particular: a well-level profile never requires cell_id (IDENT004),
and a treatment-level profile never requires plate/well directly
(IDENT001/IDENT002) as long as IDENT006 doesn't fire instead.
The original design brainstorm also sketched an early MATRIX003 meaning
("dtype vs. declared processing stage") and FEAT002 (duplicate feature
name — already covered by INDEX002). Neither was ever shipped, so both
codes were free to reassign without violating the "never renumber or
reuse" rule: MATRIX003 was later shipped with its current meaning
(.X shape vs. (n_obs, n_vars)); FEAT002 was later shipped with its
current meaning (unrecognized measurement family).