Fix TUI Grok subscription rejected with credit-limit error despite "(key connected)" - #14774
Fix TUI Grok subscription rejected with credit-limit error despite "(key connected)"#14774warp-agent-staging[bot] wants to merge 2 commits into
Conversation
The model picker's "(key connected)" suffix for xAI came from `is_using_api_key_for_provider`, which checked the loose `grok_tokens().is_some()`. The request-time token attach (`GrokTokens::access_token_for_request`) and the credit-availability gate (`has_usable_byo_inference_path` -> `ApiKeyManager::has_any_key` -> `has_grok_subscription`) both require a *usable* (non-blank) access token. A stored Grok token with a blank access token therefore rendered as "(key connected)" while nothing was sent on the wire and the credit gate treated the subscription as absent, producing the reported Warp credit-limit rejection despite the picker promising a connected key. Single-source the xAI predicate on `has_grok_subscription()` so the picker, the request attach, and the credit gate all agree. Co-Authored-By: Warp <agent@warp.dev>
There was a problem hiding this comment.
Overview
This PR single-sources the xAI (key connected) predicate on has_grok_subscription() so the model picker can't claim a usable Grok subscription while the credit gate treats it as absent (acceptance criterion #2); the change itself is correct, minimal, and well-tested. Requesting changes: the required Formatting + Clippy CI job is red, and there's an open question about whether this client-only fix actually resolves the reporter's server-originated symptom.
Concerns
❓ [QUESTION] @jefflloyd — the reported failure was a server-originated out-of-credits error, so the request reached the server with no usable Grok token on the wire; this change makes (key connected) honest and the credit gate (has_any_key → has_grok_subscription) already honors a usable subscription, which fully covers cause #1 (a blank local token over-promising in the picker) but not cause #2 (server strips GrokOauthAccessToken when CanUseByoKey is false) or cause #3 (cross-process reload_keys_from_secure_storage never re-reads Grok tokens). Under cause #2 the picker would still show (key connected) for a usable token and the server would still reject, so the reporter's symptom could persist. Since disambiguating needs the reporter's wire data / a production SuperGrok account (unavailable to the agent), can you — as the REV-1714 author — confirm which cause matches the report before merge, so we know this resolves the reporter's issue rather than only aligning the picker label?
A blocking implementation-addressable finding (the failing Formatting + Clippy CI job — an import-ordering diff in app/src/ai/llms_tests.rs, fixed by running ./script/format) is being relayed to implementation automatically for rework.
Verdict
Checks: fmt ❌ (import-ordering diff in app/src/ai/llms_tests.rs, reproduced via ./script/format --check) · CI ❌ (Formatting + Clippy failing on Linux/MacOS/Windows) · build/tests not run locally (heavy crate; CI is the backstop) · visual proof n/a (documented production-only SuperGrok + out-of-credits repro; picker/credit-gate behavior covered by unit tests)
Found: 1 critical, 0 important, 0 suggestions, 1 open question
Request changes
Review run
https://oz.staging.warp.dev/runs/019fd510-44f2-7c46-a3b4-11f4d6ee94ce
The added MockTelemetryContextProvider import must sit in the single external-crate group (group_imports=StdExternalCrate), sorted before `warpui::App` with no separating blank line. Applied via ./script/format. Co-Authored-By: Warp <agent@warp.dev>
Summary
With a connected X Premium / SuperGrok subscription,
grok *models in the TUI model picker render(key connected), but sending a request is rejected with the Warp credit-limit error ("You've reached your credit limit…"). QUALITY-1508.Root cause: the picker's
(key connected)suffix for xAI comes fromis_using_api_key_for_provider(app/src/ai/llms.rs), which checked the loosemanager.grok_tokens().is_some(). But the request-time token attach (GrokTokens::access_token_for_request) and the credit-availability gate (has_usable_byo_inference_path→ApiKeyManager::has_any_key→has_grok_subscription) both require a usable (non-blank) OAuth access token. A stored Grok token whose access token is blank therefore rendered as(key connected)while nothing was sent on the wire and the credit gate treated the subscription as absent — the server then fell through to the Warp credit gate and rejected, exactly as reported.Fix: single-source the xAI "usable Grok subscription" predicate on
has_grok_subscription()so the picker, the request-time attach, and the credit gate all agree. This makes it impossible for(key connected)to appear while the credit/inference path treats the subscription as absent (acceptance criterion #2). The credit-availability decision already recognizes a connected Grok subscription (viahas_any_key()), and the request-time attach gates the token on the same BYO policy, so no change was needed there; this change removes the remaining inconsistency.Scope note (possible warp-server component — not changed)
Triage flagged a possible server-side cause (the server stripping
GrokOauthAccessTokenwhenCanUseByoKeyis false). Reviewing the client wire path, the client attaches the Grok token whenever BYO keys are enabled and the token is usable, and the credit gate already honors a connected subscription — so the confirmed, reproducible-in-code defect is client-side. The server-strip hypothesis remains unproven and is out of scope for this PR; per the ticket it hinges on the reporter's wire data, which isn't available here. No warp-server change is opened on speculation.Verification
xai_key_connected_requires_usable_grok_access_tokeninapp/src/ai/llms_tests.rs— assertsis_using_api_key_for_provider(&LLMProvider::Xai, …)isfalsefor a connectedGrokTokenswith a blank access token andtruefor a usable one (with BYO enabled). Verified it FAILS against the pre-fix predicate and PASSES with the fix.test_out_of_credits_refined_by_connected_grok_subscriptioninapp/src/ai/request_usage_model_tests.rs— under a serverOutOfCreditsdecision, a connected Grok subscription permits AI, a blank token does not, and BYO/Bedrock refinement is unaffected (acceptance criteria Search history by more than just command #1 and Shared environment variables and secrets #3).cargo nextest run -p warp -E 'test(request_usage_model) + test(llms) + test(prompt_alert)'→ 91 passed.cargo fmt -p warp(clean) andcargo clippy -p warp --all-targets --tests -- -D warnings(clean).Visual proof not obtainable
This touches a user-facing TUI surface (the model picker suffix and the agent error state), but a live repro needs a logged-in account with a connected SuperGrok OAuth subscription in an out-of-Warp-credits state against a live server — production-only credentials/infra not available in this environment. Per the ticket, hands-on visual proof is deferred; the behavior is covered by the deterministic tests above.
CHANGELOG-BUG-FIX: Fix Grok subscription models in the TUI showing "(key connected)" but being rejected with the Warp credit-limit error
Originating thread: https://warpdev.slack.com/archives/C0BDQDW8V5E/p1785983338928669
This PR was generated with Oz.