Forecast crashed on the Flight path because a historical fetch that predates the DuckDB cache window returns RepoError::MethodNotSupported, which originally mapped to gRPC FailedPrecondition and was treated as fatal by the py-mando client. The fix makes Flight degrade gracefully: stream what the cache window can serve, fall back to REST for ranges that predate it.
For Agents
This is the resolution of the “Known limit” flagged in be-1595-flight-execution-id-parity-2026-06-17 — manual-override / historical reads predating the DuckDB cache window returning
MethodNotSupportedfromrepo_passthrough.rs:335. That note diagnosed it; this note fixes it (commit7926278c).
The capability gap
The Arrow Flight passthrough / DuckDB repo can only stream data within its cache window. A retrieve_stream over a range that predates the cache — i.e. data that lives only in Permanent storage — cannot be served by the streaming path and returns RepoError::MethodNotSupported (repo_passthrough.rs:335). REST does not have this limitation: it serves the same range from permanent storage.
So this is a genuine capability gap, not an error: Flight can’t do historical, REST can. The right behavior is to fall back, not to fail.
Symptoms
- Optimization worked fully on Flight (it only fetches within the cache window).
- Forecast crashed when
MANDO_FETCH_STRATEGY=flight— it fetches historical data for its lookback, which predates the cache.
Root cause (the fatal classification)
Two pieces conspired to turn a recoverable capability gap into a fatal error:
- Server side —
repo_error_to_status(mando-bess/src/flight.rs) mappedRepoError::MethodNotSupported→ gRPCFailedPrecondition. - Client side — the py-mando client only fell back to REST when the error was
is_retryable()(transport-level errors). AFailedPreconditionis not retryable, so it propagated asFataland crashed forecast.
Fix (commit 7926278c)
Reclassify the capability gap so the client fallback recognizes it:
| Layer | File | Change |
|---|---|---|
| Server | mando-bess/src/flight.rs (repo_error_to_status) | Map RepoError::MethodNotSupported → gRPC Unimplemented (was FailedPrecondition) |
| Client error | mandarrow-client/src/error.rs | Add ClientError::is_unimplemented() |
| Client fallback | py-mando/src/polars.rs | Fall back to REST on `is_retryable() |
Keep
CyclicDependencyasFailedPreconditionDo NOT blanket-remap everything to
Unimplemented.RepoError::CyclicDependencyis a real error that REST cannot fix either — it must stayFailedPreconditionso it propagates as fatal. Only the capability gap (MethodNotSupported= “this transport can’t serve this range”) becomesUnimplemented. The semantic line:Unimplemented= “wrong transport, try the other one”;FailedPrecondition= “the request itself is bad, no transport will help”.
Net behavior after the fix
- Flight streams everything it can (cache-window fetches).
- For ranges that predate the cache, the server returns gRPC
Unimplemented, the client recognizes it viais_unimplemented(), and falls back to REST for that fetch. - Optimization: works fully on Flight (cache-window fetches only — never hits the gap).
- Forecast: logs
falling back to REST(gRPC Unimplemented) then completes successfully.
How to verify in logs
A healthy forecast run on Flight will emit a
falling back to RESTlog line (gRPC Unimplemented) for the historical portion, NOT a crash. Seeing that line is the expected signal that graceful degradation is working — distinct from the silent REST fallback on transport errors.
Related
- be-1595-flight-execution-id-parity-2026-06-17 — diagnosed this as a “Known limit”; resolved here
- be-1595-enabling-arrow-flight-consumers-2026-06-16 — the runtime switch + the other (silent) REST fallback path
- be-1595-flight-wire-type-version-skew-2026-06-24 — the other deploy hazard on this feature
- be-1595-arrow-flight-dev-deploy-runbook-2026-06-24 — full dev deploy runbook
- Arrow Flight Streaming Design
- py-mando