Diagnose a forecast the tool refused, repaired, or never returned. Use when a run dies with "returned an unusable forecast", when a model command hangs with no output, or when stored quantiles look mislabelled.
Install
npx skillscat add eliquid/predict-marketing-forecasting/refused-forecast Install via the SkillsCat registry.
A forecast that was refused, repaired, or never came back
Read AGENTS.md §4 first — it defines the protocol, and §5 the validation rules
this is the debugging side of. AGENTS.md §2a1 records the real incident that
makes this a recurring job: one long-dead campaign came back 6.9% out of order,
failed validation, and took a whole import down with it.
Nothing here is a fix. Work out which of the four causes you have, then change
the thing that is actually wrong.
Read the message properly first
chronos2, Patches DSA #2 - Zombie: model "chronos2" returned an unusable
forecast for "Cost": day 0: quantiles are badly out of order (largest crossing
44.4% of the forecast's own size, far beyond the ~0.2% a model normally produces)Three things it does not say, and one it says misleadingly:
day 0is an offset into the horizon, not a date. Day 0 is the first
forecast day — tomorrow relative toas_of, not the last day of history.- The entity comes from the caller, not the validator. In
importit is themodel, entity:prefix; inforecastit is the bareentity:prefix. A
message with no prefix came from somewhere that lost it. - Nothing is stored. A failed forecast fails the whole run, so there is no
half-written row to clean up — but inimportit also kills the import, and
report 1 is only safe if it had already been written. - "quantiles are badly out of order" blames the forecast, and the forecast is
not always what is wrong. See cause 3.
Cause 1 — the entity has nothing to forecast
By far the most common, and the one AGENTS.md §2a1 is about. A campaign that
stopped spending comes back as float noise, and noise has no reliable ordering.
# is it switched off in the export?
./predictmarketing forecast YOUR.csv -model chronos2 -entities "THE NAME" -db /tmp/d.dbIf it is switched off, that message tells you so instead of forecasting. If it is
switched on and dead anyway, the export's status column is not being believed —
check the column is found by its values, not its name (campaignStates iningest.go, §2a1), and that CAMPAIGN_STATES in models/finetune.py still
matches it.
Cause 2 — the crossing is real and the model is struggling
Re-run the same entity alone and look at what comes back:
./predictmarketing forecast YOUR.csv -model chronos2 -entities "THE NAME" \
-horizon 7 -db /tmp/d.dbforecast prints a note when it repaired a crossing; import does not report
one at all, so a single-entity forecast is the only way to see the size of a
repair. If the refused metric is near zero for the whole window, you are back at
cause 1.
Cause 3 — the worker's declared quantiles do not match its reply
Only reachable when someone has added or edited a worker, and it is the cause the
message hides. checkForecast sorts each day ascending as its repair, and the
stored quantile label comes from the handshake's declared order by position. So
a worker that declares [0.5,0.1,0.9], or that returns its columns in an order
other than the one it declared, is either refused with a message about the
forecast (wide intervals) or silently relabelled (narrow ones).
./predictmarketing models # the declared list, per model
models/.venv/bin/python models/NAME_worker.py < one-request.jsonl | head -2The declared list must be ascending, and the reply's columns must be in that same
order. Chronos-2 honours quantile_levels in whatever order it is handed, so it
will not correct a mistake for you; TimesFM 3.0 refuses any list but its own nine.
Cause 4 — no answer at all
A model command that sits silent is not always slow. The 5-minute timeout covers
the reply only; the handshake read has no deadline, so a worker that stalls
while loading hangs forever with nothing after the library's progress bar.
- Output stops right after
Loading weights: …and never returns → the worker is
stuck before its handshake.Ctrl-Cand run the worker by hand:models/.venv/bin/python models/NAME_worker.py < /dev/null— it should print
one JSON line and exit. - The message is "did not answer within 5m0s … stuck rather than busy" → the
handshake worked and the forecast itself hung. The worker has been killed;
the process is gone, not leaked. - "exited before saying hello" or "could not start: …" → the worker's own last
explanation is in the message. Read it; it is usually a missing adapter, a
missing environment, or a failed weights checksum.
Do not do these
- Do not widen
absurdCrossingto make a refusal go away. 5% is far beyond
the ~0.2% both models actually produce; a forecast past it is broken, and the
rule is inAGENTS.md§5. - Do not lower the
1e-6floor incheckForecastto catch something. It is
absolute and in the CSV's own units, and lowering it re-opens the defect inAGENTS.md§9 where a flat-zero series was refused for crossing "10%" between
two values a billionth apart. - Do not skip the entity.
AGENTS.md§5: unusable input is named, never
quietly dropped. If an entity should not be forecast, it should be excluded by
the status rule (§2a1), where the reason is recorded and printed.