{"record":{"id":"0013c3a77e8af144","repo":"xai-org/grok-build","slug":"billing-service-error-detail","errorCode":null,"errorMessage":"Billing service error: {detail}","messagePattern":"Billing service error: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codegen/xai-grok-shell/src/extensions/billing.rs","lineNumber":258,"sourceCode":"        let status = credits_resp.status().as_u16();\n        let body = credits_resp.text().await.unwrap_or_default();\n        tracing::warn!(status, url = %credits_url, \"billing: upstream error\");\n\n        let detail = serde_json::from_str::<serde_json::Value>(&body)\n            .ok()\n            .and_then(|v| v.get(\"error\").and_then(|e| e.as_str()).map(String::from))\n            .unwrap_or_else(|| format!(\"HTTP {status}\"));\n\n        xai_grok_telemetry::unified_log::warn(\n            \"billing: upstream error\",\n            None,\n            Some(serde_json::json!({\n                \"status\": status,\n                \"detail\": detail,\n            })),\n        );\n\n        return Err(acp::Error::internal_error().data(format!(\"Billing service error: {detail}\")));\n    }\n\n    let mut billing: BillingConfigResponse = credits_resp.json().await.map_err(|e| {\n        tracing::error!(error = %e, \"billing: failed to parse response\");\n        xai_grok_telemetry::unified_log::warn(\n            \"billing: failed to parse response\",\n            None,\n            Some(serde_json::json!({ \"error\": e.to_string() })),\n        );\n        acp::Error::internal_error().data(format!(\"Failed to parse billing data: {e}\"))\n    })?;\n\n    // Enrich with fields from remote settings.\n    let rs = agent.cfg.borrow().remote_settings.clone();\n    billing.on_demand_enabled = rs.as_ref().and_then(|rs| rs.on_demand_enabled);\n    billing.subscription_tier = rs.as_ref().and_then(|rs| {\n        rs.subscription_tier_display\n            .clone()","sourceCodeStart":240,"sourceCodeEnd":276,"githubUrl":"https://github.com/xai-org/grok-build/blob/bc7f02eddd3d84085849dc19ed216f11c23b0571/crates/codegen/xai-grok-shell/src/extensions/billing.rs#L240-L276","documentation":"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.","triggerScenarios":"`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`.","commonSituations":"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.","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."],"exampleFix":"// before: stale token -> 401\ncurl https://api.x.ai/credits -H \"Authorization: Bearer $OLD\"\n// after: refresh the token then retry\ncurl https://api.x.ai/credits -H \"Authorization: Bearer $FRESH\"","handlingStrategy":"try-catch","validationCode":"// check auth and endpoint before the call\nif (!token || isExpired(token)) throw new Error(\"refresh billing credentials first\");\nconst probe = await fetch(creditsUrl, { headers: auth(token) });\nif (probe.status === 401 || probe.status === 403) throw new Error(\"billing auth rejected: \" + probe.status);","typeGuard":null,"tryCatchPattern":"try {\n  const billing = await acp.request(\"billing_get\");\n} catch (e) {\n  const m = String(e.message).match(/^Billing service error: (.*)$/);\n  if (m) {\n    console.error(\"upstream status/detail:\", m[1]);\n    if (m[1].includes(\"401\") || m[1].includes(\"403\")) await refreshCredentials();\n  } else { throw e; }\n}","preventionTips":["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"],"tags":["http","billing","upstream-error","http-status"],"backgroundTag":"upstream-http-error","analyzedSha":"bc7f02eddd3d84085849dc19ed216f11c23b0571","analyzedAt":"2026-08-31T04:59:42.031Z","schemaVersion":2},"datasetVersion":"2026-08-31T09:17:48.483Z"}