xai-org/grok-build · error
Billing service error: {detail}
Error message
Billing service error: {detail} What it means
The billing endpoint responded, but with a non-success HTTP status. The handler reads the body, tries to extract a `detail` message from the JSON, and returns an ACP internal error 'Billing service error: {detail}' with status and detail attached as telemetry data. This means the upstream billing service rejected the request rather than the transport failing.
Source
Thrown at crates/codegen/xai-grok-shell/src/extensions/billing.rs:258
let status = credits_resp.status().as_u16();
let body = credits_resp.text().await.unwrap_or_default();
tracing::warn!(status, url = %credits_url, "billing: upstream error");
let detail = serde_json::from_str::<serde_json::Value>(&body)
.ok()
.and_then(|v| v.get("error").and_then(|e| e.as_str()).map(String::from))
.unwrap_or_else(|| format!("HTTP {status}"));
xai_grok_telemetry::unified_log::warn(
"billing: upstream error",
None,
Some(serde_json::json!({
"status": status,
"detail": detail,
})),
);
return Err(acp::Error::internal_error().data(format!("Billing service error: {detail}")));
}
let mut billing: BillingConfigResponse = credits_resp.json().await.map_err(|e| {
tracing::error!(error = %e, "billing: failed to parse response");
xai_grok_telemetry::unified_log::warn(
"billing: failed to parse response",
None,
Some(serde_json::json!({ "error": e.to_string() })),
);
acp::Error::internal_error().data(format!("Failed to parse billing data: {e}"))
})?;
// Enrich with fields from remote settings.
let rs = agent.cfg.borrow().remote_settings.clone();
billing.on_demand_enabled = rs.as_ref().and_then(|rs| rs.on_demand_enabled);
billing.subscription_tier = rs.as_ref().and_then(|rs| {
rs.subscription_tier_display
.clone()View on GitHub (pinned to bc7f02eddd)
Solutions
- Read `{detail}` from the error (and the `status` in the telemetry data) to see exactly what the billing service rejected.
- Re-authenticate or refresh credentials if the status is 401/403.
- If 429, back off and retry later; if 5xx, check billing service health and retry after the outage.
- Verify the configured credits URL matches the current billing API path in remote settings.
Example fix
// before: stale token -> 401 curl https://api.x.ai/credits -H "Authorization: Bearer $OLD" // after: refresh the token then retry curl https://api.x.ai/credits -H "Authorization: Bearer $FRESH"
Defensive patterns
Strategy: try-catch
Validate before calling
// check auth and endpoint before the call
if (!token || isExpired(token)) throw new Error("refresh billing credentials first");
const probe = await fetch(creditsUrl, { headers: auth(token) });
if (probe.status === 401 || probe.status === 403) throw new Error("billing auth rejected: " + probe.status); Try / catch
try {
const billing = await acp.request("billing_get");
} catch (e) {
const m = String(e.message).match(/^Billing service error: (.*)$/);
if (m) {
console.error("upstream status/detail:", m[1]);
if (m[1].includes("401") || m[1].includes("403")) await refreshCredentials();
} else { throw e; }
} Prevention
- Refresh auth tokens proactively before expiry
- Verify account has billing access and the credits URL path is current
- Back off on 429 and alert on repeated 5xx from the billing service
- Log status + detail from the error data for faster triage
When it happens
Trigger: `credits_resp.status().is_success()` is false — e.g. 401/403 for missing or expired auth token, 404 from a wrong/changed billing URL, 429 rate limiting, or 5xx from the billing backend — inside `handle_get_billing`.
Common situations: Expired or missing API credentials/session token; account without billing access; the credits endpoint moved and remote settings still point at the old path; upstream outage returning 502/503.
Related errors
- Failed to fetch billing data: {e}
- send failed: {body}
- screen query failed: {body}
- resize failed: {body}
- wait failed: {body}
AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31).
Data as JSON: /api/errors/0013c3a77e8af144.
Report an issue: GitHub.