grok-cli-runtime
Grok Auth And Quota
Every lane runs the same grok binary against the same $GROK_HOME (default ~/.grok) and the same xAI quota. T3 Code spawns it as grok agent stdio (ACP) for the grok provider instance; a headless sidecar runs grok -p. The transport differs — the auth file, the billing, and the failure codes below do not.
Invocation is not this skill's job. When T3 Code runs Grok, it owns the process, session, and permissions. This skill is what to do when that run comes back red.
Read the failure code before you touch anything
401— missing or invalid authentication. Real auth problem; continue to the gate.402 Payment Required(Grok Build usage balance exhausted) — quota, not auth. The credential is valid and there is nothing to recover: no probe, no rotation, and nevergrok login. Logging in again cannot buy quota, and it risks a working auth file to fix a billing condition. Read the reset time from the provider usage window and route the task to another model until then.403 permission-denied— xAI rejected the selected credential or team for that endpoint. Wrong principal/policy, a stale cloud seed, or transient provider-side state. It does not by itself prove a lapsed subscription.
Auth precedence in the CLI: per-model api_key → per-model env_key → active session token → global XAI_API_KEY. A plain global key does not override a working OAuth session. Treat auth.json as an opaque whole file: never extract, merge, or document its fields.
Empty output is a failure to diagnose, never an empty answer
A failed run still exits the JSON contract. It prints {"type":"error","message":"…"} with exit 1 and carries no .text and no .stopReason. A caller that reads only .text gets null and reports "no assistant output" — indistinguishable from a model that said nothing. Observed 2026-08-04: a quota exhaustion presented as a mystery DOA for hours.
Check .type before .text. The reason is always in .message. Exit code cuts both ways: a refusal or tool loop exits 0 with real .text, so exit 0 is not proof of a finished reply either — check .stopReason is end_turn (or the older EndTurn).