Skip to content

FAQ and troubleshooting

Common questions, every error message you are likely to hit, and what to do about each.

If a term here is unfamiliar, the Glossary has it.


Getting started

How long before this is actually useful?

That depends entirely on archive age, and it is the binding constraint on everything.

Milestone Requires
qc and build-dataset work a station DB and any forecast archive
predict works one provider forecast within the last 12 hours
backtest produces folds issue span greater than initial_train_days — more than 90 days by default, plus usable data
Promotions start clearing the gate more, and the exact amount depends on your slice sizes

Until then predict will run and emit a degraded forecast — an ungrounded equal-weight blend. That is a real, usable forecast; it is just not the evidence-backed one.

The archive is the part you cannot rush. You cannot ask a provider what it predicted last Tuesday. Start the polling cron today, even if you plan to do nothing else for three months. See Scheduling.

Can I run this without waiting three months?

Partly. backfill fetches archived forecasts from open-data sources to build a synthetic archive you can backtest against immediately:

grounded-weather-forecast backfill --provider open_meteo --start 2024-01-01
grounded-weather-forecast backtest --source synthetic

Two honest caveats. Open-Meteo's Previous Runs API gives instantaneous leads at exact 24-hour multiples; rain interval starts have leads of 23, 47, ... hours. The 0–6 h range where anchoring earns its keep stays entirely unevaluated. And it covers three open NWP models, so the resulting leaderboard says nothing about the commercial providers you will actually serve from.

Synthetic and live results are never pooled. Read Limitations §3 before drawing conclusions.

Using --provider dynamical gets you native sub-24h leads, at the cost of an optional dependency (uv sync --extra backfill).

Can I use a different station, or no station at all?

A different station: yes. Map your columns and units in [station.columns] and [station.units] — see the configuration reference. The pipeline needs a monotone event rain counter for precipitation; a field already reporting hourly accumulation is a different quantity and will need code changes.

No station: no. The station is not an optional extra — it is the input no provider has, and grounding and anchoring both depend on it. Without truth there is also nothing to score against, so the leaderboard has nothing to arbitrate.

Do I need API keys?

Not for the core system. The upstream collector needs whatever keys its providers require, but this project reads SQLite files.

Optional: truth_qc uses NWS METAR neighbours with no key, and can additionally use Synoptic Data with a free-tier token supplied as "$SYNOPTIC_TOKEN".


Error messages

cannot predict: no provider forecast within 12.0h of ...

Your archive's most recent forecast is older than max_forecast_age_hours, so the system refuses to serve a stale forecast rather than pretending it is current.

Either the polling cron is not running, or you are testing against an old archive. To reproduce a document as of a past instant:

grounded-weather-forecast predict --now 2026-03-22T17:00:00

no rolling-origin folds. The archive spans 0.0 days ...

backtest found nothing to test. Not a failure — the system being honest. Backtesting means "train on the past, test on the future, repeatedly", which needs history. With a more-than-90-day first-origin requirement and a one-day-old archive there is nothing to do.

Start the cron, and optionally backfill so you can measure something today.

MixedProvenanceError

Something tried to combine live and synthetic rows. This is a guard, not a bug — see Methods: verification §7. Pass a single --source.

ContractViolationError: ... starts with 't__'

A truth column reached a feature matrix. This is leakage defence #4 firing. If you are adding a blender or a feature, the column naming rules are in Methods: notation §3.

A ConfigError naming a key

Configuration is validated at load with explicit messages. The named key has the wrong type, is out of range, or is not one of the allowed choices — check it against the configuration reference.

unknown method 'x'; available: ...

A --methods value or a [predict.methods] pin names an unregistered method. The error lists every valid id.

A channel is 100% null after qc

Your [station.columns] mapping is wrong — the named DB column does not exist or holds something else. This is exactly what qc is for; fix it before running anything else, because every later command will fail more confusingly.


Interpreting output

Why does the truth-QC panel warn after a successful report?

A report refreshes the dashboard, not the retained QC verdict. The panel and central alert list warn after a failed QC refresh, when the verdict is over 48 hours old, or when its freshness is unknown. Individual chips keep their own conditions: a quiet verdict stays green, an unknown refresh time is amber, and a failed attempt does not change a fresh age’s color. Failed attempts display the bounded message saved in the run ledger. Latched drift remains red, and these operational warnings do not change fitting quarantine.

Why does my forecast say degraded?

With automatic selection, status: "degraded" means no finite hourly or daily value retains a compatible promoted release. Matching dataset, config, and code fingerprints is necessary, but the method must also produce a finite attributed value. Zero is finite; null, NaN, and infinite outputs cannot justify readiness. Serving may use equal_weight or native rainfall containment instead. Explicit --method runs keep their existing ready status without requiring release attribution. status_reason explains automatic degradation. Common ones:

Reason Fix
no backtest evidence for this slice not enough archive yet — wait, or backfill
implementation changed since the last backtest run recover, or re-run backtest --source live then report
no compatible approval after a dataset layout change run recover, which rebuilds before backtesting; data-only fingerprint rotation preserves compatible approvals
configuration changed run recover, or re-run the live backtest and report
some compatible promoted selections could not be fitted at issuance inspect the named product/variable and cause: poll as live issuance advances for pending truth; later-issued rows remain unavailable at fixed historical --now; build missing matrices or incorporate valid truth as advised
live evidence exists but no promoted release produced a finite hourly or daily forecast inspect per-field reasons and provider availability; this does not mean a release was never promoted
rainfall safeguard removed attribution inspect wet-day counts in the affected fields; automatic rainfall needs five distinct wet training days per bucket
rainfall reconciliation removed attribution inspect native minute, hourly, and observed rainfall; the adjusted value cannot inherit the model's release or uncertainty

The pattern: anything that changes the system's identity invalidates the evidence that justified a promotion. Re-running backtest then report re-earns it. See ADR 0005.

Actual wet-day guard loss takes precedence over actual reconciliation loss. Otherwise archive absence and identity changes keep their existing diagnoses; fitting failures are named only when evidence identities match. The message lists only unavailable promoted fits in stable product/variable/semantics order; it does not claim every fit failed. Existing finite truth excluded by the issue-time cutoff can become eligible on a later prediction without any dataset rebuild. Cold-start blends and native floors filling unattributed nulls record no rainfall attribution loss. Detailed field reasons retain wet-day selection history even when the document names unavailable fitting. Null hourly/daily points omit quantiles and their source; a quantile source always accompanies usable emitted quantiles.

Scheduled publish attempts that recovery automatically, at most once per unchanged degradation signature every six hours. While it runs, an existing ready published document is held byte-for-byte.

Degraded status prints to stderr, so --out - still gives clean JSON on stdout.

Why is n so small on my leaderboard?

n is the number of scored cases in that slice, and it is printed precisely so a thin slice announces its own lack of power rather than hiding behind a p-value.

Small n usually means: a short archive, a long lead bucket (a 240h+ bucket needs 10 days of archive per case), a variable few providers publish, or a method that abstained on most rows because it could not fit. The last is by design — see Methods: notation §6.

Eligibility requires n ≥ 8 and n_valid_times ≥ 8 before any promotion gate even runs.

Why did my served method change overnight?

Promotions are re-decided every report. Some churn is legitimate — new evidence arrived — and some is noise. The system tracks this: reports/selection_churn.md reports how often the served method changes, and a high churn rate is itself a signal that slices are too thin to decide.

The promotion gate exists to suppress exactly this. If churn is high, consider whether [promotion] rule should be the stricter seq_mcs.

Why is a method missing from a slice entirely?

Either it is scoped out (precipitation heads are not offered temperature), or it abstained — below its _MIN_FIT_ROWS, or lacking wet cases, or without a short-lead anchor row. Abstention is a first-class outcome here: a method that cannot fit says so rather than fitting badly on twelve rows.

The leaderboard says equal weight is winning. Is something broken?

Probably not. A plain average of grounded sources being within a hair of the best method is the forecast combination puzzle, a well-documented empirical result: estimating weights adds variance faster than it removes bias. On this data raw equal weight wins the 96–168 h temperature slice outright.

See Concepts §3.

anchored_* methods have identical scores to their base

Expected on synthetic data. Anchoring needs a forecast row with lead under 6 hours to compute its residual, and the Previous Runs backfill starts at 24 h. With no anchor row the τ search selects "no anchoring" and the wrapper degrades exactly to its base. The mechanism works; that data cannot exercise it.


Operations

Where does each command write?

Summarized in Outputs; per-command in the CLI reference.

What are the exit codes?

Code Meaning
0 success
1 command-level failure — missing inputs, no scores, a backfill error
2 configuration error, or unknown command

truth-qc deliberately returns 0 when it finds no evaluable checks: a cold start is not a fault and should not page anyone.

What is safe to delete?

reports/ entirely — regenerated by report, alignment, and truth-qc. data/scores/ via prune-scores (use --dry-run first).

Not safe: data/predict_history.parquet (the record of what you served), artifacts/history/ (append-only ledgers), and artifacts/eprocess/ (sequential accumulated evidence — deleting it restarts every gate from zero wealth). And above all, the two upstream SQLite files, which cannot be recreated.

See Outputs.

report is slow / disk is filling up

report iterates every scores file, and expanding-window backtests accumulate them. Run prune-scores; retention keeps the newest per group, everything the active release depends on regardless of age, and anything a release from the last 7 days depends on.

How do I reproduce a forecast the system served last week?

grounded-weather-forecast predict --now 2026-03-22T17:00:00 --no-history

--now reissues from an archived snapshot using only data available by that instant. Add --no-history so the experiment does not pollute the self-verification record.

How do I force one method for testing?

grounded-weather-forecast predict --method gbm --no-history

Or pin per slice in [predict.methods]. Both bypass the promotion gate — a deliberate override of the system's own evidence, not a tuning knob. Always pair ad-hoc runs with --no-history.


Extending it

How do I add my own blending method?

Implement the Blender protocol and register a factory (never an instance — the engine builds a fresh blender per fold as a leakage defence). Worked example in Advanced usage; the protocol is in Methods: notation and the API reference.

Then let the leaderboard decide. Nothing ships because it sounds good.

Can I use this as a library?

Yes — see Advanced usage and the API reference. contracts, config, blenders.protocol, blenders.registry, metrics, leads, and serve.schema are the intended surface.