fix(usage): surface active-provider fetch errors instead of dropping them - #759
Merged
Conversation
…them
`fetch_all` spawned each active provider as `fetch.call().ok()` and merged
results with `filter_map(|h| h.join().ok().flatten()).flatten()`, so any
provider that returned `Err` was silently discarded. The most visible case
is sakana::fetch building a "refresh SAKANA_SESSION_COOKIE" auth error: the
provider's `has_credentials()` still reports it active, yet it just vanished
from output — contradicting docs/providers/sakana.md and sakana's own error
path.
Root cause: errors were thrown away at the join/merge step, with no channel
to carry them back to the user.
Fix: add `fetch_all_with_errors()` returning both successful outputs and a
`Vec<ProviderError>` (provider name + formatted message). The merge logic is
extracted into a pure, unit-testable `partition_results`. The CLI `run` uses
the new function and prints one concise warning per failing provider to
stderr in non-JSON mode ("Sakana: <error> — skipped"). JSON mode emits no
warnings so `--json` consumers reading stderr are not corrupted. `fetch_all`
is kept as a thin error-discarding wrapper so the TUI caller is unchanged.
Confidence: high
Scope-risk: narrow
Not-tested: real provider network/auth round-trip (covered only by the pure partition test)
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
makoMakoGo
added a commit
to makoMakoGo/tokscale
that referenced
this pull request
Jun 23, 2026
ported from upstream junhoyeo#728 ported from upstream junhoyeo#757 ported from upstream junhoyeo#759 ported from upstream junhoyeo#760
t1000040
pushed a commit
to tmobi-internal/tokscale
that referenced
this pull request
Jun 30, 2026
…them (junhoyeo#759) `fetch_all` spawned each active provider as `fetch.call().ok()` and merged results with `filter_map(|h| h.join().ok().flatten()).flatten()`, so any provider that returned `Err` was silently discarded. The most visible case is sakana::fetch building a "refresh SAKANA_SESSION_COOKIE" auth error: the provider's `has_credentials()` still reports it active, yet it just vanished from output — contradicting docs/providers/sakana.md and sakana's own error path. Root cause: errors were thrown away at the join/merge step, with no channel to carry them back to the user. Fix: add `fetch_all_with_errors()` returning both successful outputs and a `Vec<ProviderError>` (provider name + formatted message). The merge logic is extracted into a pure, unit-testable `partition_results`. The CLI `run` uses the new function and prints one concise warning per failing provider to stderr in non-JSON mode ("Sakana: <error> — skipped"). JSON mode emits no warnings so `--json` consumers reading stderr are not corrupted. `fetch_all` is kept as a thin error-discarding wrapper so the TUI caller is unchanged. Confidence: high Scope-risk: narrow Not-tested: real provider network/auth round-trip (covered only by the pure partition test)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
fetch_allincrates/tokscale-cli/src/commands/usage/mod.rsspawned each active provider asfetch.call().ok()and collapsed the results withfilter_map(|h| h.join().ok().flatten()).flatten(). Any provider that returnedErrwas silently dropped.The clearest failure case is Sakana:
sakana::fetchdeliberately builds a helpful "Sakana session expired or invalid. RefreshSAKANA_SESSION_COOKIE…" error when the session cookie is stale. But becausehas_credentials()returnstrue(a cookie exists, it's just expired), the provider is considered active — yet it just disappeared from the output with no message. This contradicts bothdocs/providers/sakana.mdandsakana::fetch's own carefully-worded error path. The same silent-drop affected every usage provider.Root cause
Errors were discarded at the join/merge step (
.ok()+filter_map), with no channel to carry them back to the user.Fix
fetch_all_with_errors() -> (Vec<UsageOutput>, Vec<ProviderError>). Each active provider's thread now returns(name, Result<...>); successes and errors are split by a new pure helperpartition_results.pub fn runnow callsfetch_all_with_errorsand, in non-JSON mode only, prints one concise line per failing provider to stderr:"Sakana: <error> — skipped".--jsonconsumers that also read stderr are not corrupted. (Note: pre-existing unconditionaleprintln!s inminimax_tokenplan.rsalready violate this, but that's out of scope here.)fetch_allis retained as a thin error-discarding wrapper (fetch_all_with_errors().0), so the TUI dashboard caller (tui/app.rs) is unchanged. NoUsageOutputstruct change was needed.Approach / tradeoffs
I chose the least-invasive option from the brief: collect per-provider errors and surface them as a one-line stderr warning in non-JSON mode. Rejected a sweeping
UsageOutput/struct redesign (carrying an error variant through serialization and the TUI rendering path) as unwarranted for the goal of making failures visible. The TUI continues to usefetch_alland ignores errors for now;fetch_all_with_errorsis public so the TUI can adopt error display later without a signature change.Tests
partition_resultsand added two regression tests:partition_results_surfaces_provider_errors_instead_of_dropping_them— a failing Sakana provider's error (including theSAKANA_SESSION_COOKIEguidance) is surfaced, not discarded, while successful providers (including a Multi provider's several outputs) are preserved in order. This fails against the old drop-everything behavior.partition_results_reports_no_errors_when_all_succeed— success path unchanged.cargo test -p tokscale-cliandcargo clippy -p tokscale-cli --testsare clean for this file (remaining clippy warnings are pre-existing inreport.rs, untouched here).Residual concerns
🤖 Generated with Claude Code
Summary by cubic
Surface fetch errors from active usage providers in the
usagecommand instead of silently dropping them, so users know why a provider was skipped. Non-JSON runs now print one concise stderr warning per failing provider; JSON output is unchanged.Errinstead of discarding them at join time.fetch_all_with_errors()returning(Vec<UsageOutput>, Vec<ProviderError>)and a purepartition_resultsto split successes/errors.runto print "Provider: — skipped" to stderr in non-JSON mode only;--jsonemits clean JSON with no warnings.fetch_all()as a thin, error-discarding wrapper so the TUI remains unchanged.Written for commit bcd1785. Summary will update on new commits.