Check Report (ferx check --json)
ferx check <model.ferx> [--data <data.csv>] --json emits a structured JSON report describing every validation finding, instead of the human-readable summary. The report is designed to be consumed programmatically — by editor tooling, CI, or a coding agent authoring model files — so findings carry a stable machine-readable code rather than only prose.
This is the same data the Rust API returns from validate_model_file; see also the CLI reference for the human-readable form and exit codes.
Top-level shape
{
"valid": false,
"model": "mymodel",
"data": "data/mydata.csv",
"diagnostics": [
{
"severity": "error",
"code": "E_MISSING_COVARIATE",
"message": "Model references covariate(s) not found in data (case-sensitive): WGT. Available covariate columns: (none).",
"suggestion": "available covariate columns: (none)"
}
]
}| Field | Type | Notes |
|---|---|---|
valid |
bool | true when there are no error-severity diagnostics (warnings alone keep it true). |
model |
string | Model file stem. |
data |
string | Present only when --data was supplied. |
diagnostics |
array | Every finding; see below. May be empty. |
Diagnostic object
| Field | Type | Notes |
|---|---|---|
severity |
"error" | "warning" |
Only error affects valid and the exit code. |
code |
string | Stable identifier (see table below). |
message |
string | Human-readable description. |
block |
string | Optional. Owning model block, e.g. "error_model". Omitted when not attributable to one block. |
line |
integer | Optional. 1-based source line — see the caveat below. |
suggestion |
string | Optional. Actionable hint. |
Optional fields are omitted entirely when absent (not emitted as null).
Error / warning codes
| Code | Severity | Meaning |
|---|---|---|
E_PARSE |
error | The model file failed to parse (catch-all for parser errors). |
E_MISSING_BLOCK |
error | A required [block] is absent. block names which one. |
E_UNKNOWN_BLOCK |
error | A [block] header is not a recognised block name. The message lists every offending header with its line, the valid set this build accepts, and a did-you-mean when there is a near match; suggestion carries the near match on its own. See #1040. |
E_DEPRECATED_BLOCK |
error | A [block] that was ferx syntax and is no longer read — currently [initial_values], whose contents moved inline into [parameters]. Reported separately from E_UNKNOWN_BLOCK so the message can name the replacement instead of offering a did-you-mean that does not exist. |
E_BLOCK_INSTANCE_NAME |
error | A [block NAME] instance name is given for a block that takes none (e.g. [fit_options DOSE]), or omitted for one that requires it ([covariate_nn NAME]). |
E_BLOCK_FEATURE_DISABLED |
error | A recognised block needs a cargo feature this binary was not built with — [event_model] / [binary_model] need --features survival, [markov_model] needs --features markov. ([covariate_nn] has its own E_NN_FEATURE_DISABLED.) |
E_NN_FEATURE_DISABLED |
error | A [covariate_nn] block requires building with --features nn. |
E_MISSING_COVARIATE |
error | The model references a covariate not present in the data (case-sensitive). |
E_ETA_NOT_DECLARED |
error | An unresolved identifier reads as a random effect (ETA… / KAPPA…) but no omega / kappa declaration defines it, and the data carries no such column. Usually a deleted omega line whose exp(ETA_…) term was left behind. |
W_ETA_NOT_DECLARED |
warning | As E_ETA_NOT_DECLARED, but reported without --data, where ferx cannot yet know whether the dataset supplies the column. |
E_SAEM_NO_RANDOM_EFFECTS |
error | method = saem (anywhere in a method chain) on a model with no random effects (n_eta = 0). SAEM is an EM over the random effects; use foce / focei / laplace. |
E_METHOD_NO_RANDOM_EFFECTS |
error | method = imp, impmap or bayes (anywhere in a method chain) on a model with no random effects (n_eta = 0). Each integrates over the random effects, so with none declared the marginal collapses to the observation likelihood; use foce / focei / laplace. See #1007. |
W_GN_NO_RANDOM_EFFECTS |
warning | method = gn as the last estimating stage on a model with no random effects (n_eta = 0). Pure Gauss-Newton is start-sensitive there — with no inner EBE loop the BHHH step can collapse far from the optimum. Prefer gn_hybrid or focei. Not reported when a later stage re-optimises the GN result (gn_hybrid, or a methods = [gn, focei] chain). See #1006. |
E_PER_CMT_SCALING |
error | An observed compartment lacks a per-CMT scaling entry. |
E_PER_CMT_ERROR_MODEL |
error | An observed compartment lacks a per-CMT [error_model] entry. |
E_DATA |
error | The --data file could not be read or parsed. |
E_SDE_INCOMPATIBLE |
error | An SDE ([diffusion]) model used with an incompatible method (saem, gn, gn_hybrid) or gradient setting. |
E_IMP_CHAIN |
error | imp is mis-placed in a method chain — repeated, or (with imp_eval_only = true) not the terminal stage. The estimating imp may sit anywhere; an evaluation-only imp must be terminal. |
E_OPTIMIZER_IOV |
error | optimizer = trust_region used with an IOV model (n_kappa > 0). |
E_OPTIMIZER_AGQ |
error | optimizer = trust_region used with a quadrature stage (method = laplace, or method = focei with n_agq > 1). The trust region scores the quadrature objective but its gradient is the FOCE/Laplace closed form, so it would converge to the FOCE optimum while reporting quadrature OFVs. |
E_DOSE_ATTR_DOUBLE_USE |
error | A dose attribute the engine applies at the dose event is also read by the model, so its value is applied twice. Bioavailability and lag are caught at parse time on both engines (message names [odes], [scaling] or [adaptive_dosing]) — on ODE models via the name (F, LAGTIME/ALAG, F{n}/ALAG{n}/LAGTIME{n}), on analytical models via the pk(..., f=…) / lagtime=… mapping, where the remedy is to drop the mapping rather than rename. D{n}/R{n} are reported only when a coded RATE=-2/-1 dose in the data lands on them. Initial conditions are exempt on both engines — [odes] init(state) = … and [initial_conditions] seed the state with the raw value and never consult a dose attribute, so a seed that reads one applies it exactly once (#1046). See #993 and #1004. |
E_SIGMA_ORDER_MISMATCH |
error | A single-endpoint [error_model] names its sigmas in an order other than the [parameters] declaration order. These sigmas are consumed positionally, so the written names must match the declared order. Fix by reordering the sigma declarations (permuting a block_sigma’s lower triangle with them) — reordering the arguments also parses but swaps the proportional and additive roles, i.e. changes the model. See #1001 and Error model. |
W_STEADY_STATE_II |
warning | SS=1 doses with missing / non-positive II (treated as non-SS). |
W_STEADY_STATE_INFUSION |
warning | SS=1 infusion with T_inf > II (overlapping pulses; SS skipped). |
W_SDE_RESET |
warning | EVID=3/4 resets under an SDE [diffusion] model are not honoured. |
W_EXPERIMENTAL_SDE |
warning | The model uses stochastic differential equations ([diffusion]), an experimental feature. |
W_EXPERIMENTAL_NN |
warning | The model uses neural-network components ([covariate_nn]), an experimental feature. |
W_NEGATIVE_LAGTIME |
warning | Lag time is negative at the initial typical-value point. |
E_DERIVED_NAME_CONFLICT |
error | A [derived] name clashes with a built-in sdtab column, theta, eta, or individual-parameter name. |
W_DERIVED_COVARIATE_SHADOW |
warning | A [derived] name shadows a covariate (allowed but may be confusing). |
W_DERIVED_STEP_IGNORED |
warning | step= given for a DV-based integral (ignored; DV integrals always use observation times). |
W_ABSORPTION_TWIN_DECLINED |
warning | An analytic transit / inverse-Gaussian model’s ODE twin could not be built, so the model stays closed-form with no ODE fallback. Subjects needing the fallback (time-varying covariates, a TIME-dependent parameter, IOV, SS or infusion doses, the flip-flop regime) are then rejected with an explicit error. The message carries the twin parser’s own reason — most often an individual parameter named after a twin state (CENTRAL, PERIPH). See #1008. |
E_OUTPUT_UNKNOWN_COLUMN |
error | A name in [output] is not recognised as a covariate, individual parameter, or derived expression. |
W_OUTPUT_DUPLICATE |
warning | A name in [output] is already written to sdtab automatically (e.g. TAFD, TAD, an eta name). |
W_ADDL_MISSING_II |
warning | ADDL > 0 on a dose row but II is zero or missing; additional doses were not expanded. |
W_IOV_OCC_MISSING |
warning | Some rows in the IOV occasion column had missing or unparseable values; those rows were assigned occasion=0. |
W_MISSING_DV |
warning | One or more EVID=0 rows had a missing DV (./NA/blank) but were not marked MDV=1; they were skipped rather than scored as DV=0. |
W_DESIGN_DV |
warning | Simulation reader only: one or more EVID=0 rows had a missing DV and were kept as design points to simulate at, so the dataset contributes more rows than a fit of the same file would. |
E_NONFINITE_DV |
error | A subject carries a non-finite (NaN/Inf) observation. Usually a population read by read_population_for_simulation() — a simulation input — passed to fit(). |
E_IOV_MISSING_OCC |
error | Model declares kappa (IOV) parameters but no occasion labels were found in the dataset. Set iov_column in [fit_options]. |
Codes are stable; new ones may be added over time. Treat an unrecognised code as a generic finding of its given severity.
Line-number caveat
line is currently block-level: when present it points at the [block] header that owns the finding, not the exact offending token or column. A finding that is not attributable to a single block (for example a missing-covariate error, where the reference may appear in several blocks) omits line. A missing required block omits line too, since the block has no header in the source. Token/column-level spans are a possible future enhancement.