Hmbown/CodeWhale · error
Cloud agent sandbox listing failed
Error message
Cloud agent sandbox listing failed (HTTP {status}). What it means
Thrown by `list_job_sandboxes` when the control-plane sandbox listing request returns a non-2xx HTTP status. The dispatcher uses this listing (with JSON-encoded exact-match labels) to find/reconcile job sandboxes; a failed list means orphan detection and reconciliation cannot run. The provider status is embedded in the message for diagnosis.
Solutions
- Check the embedded HTTP status: 401/403 → refresh the cloud API key; 429 → back off and retry; 5xx → retry after the provider recovers.
- Retry the listing — it is a read-only call and safe to repeat.
- If the status is 400/422, verify the provider still accepts the JSON-encoded exact-match labels filter; update the client if the API contract changed.
- Confirm the account has an active subscription/quota; a billed-out account can reject list calls.
Defensive patterns
Strategy: retry
Validate before calling
// verify credentials before provider calls
if std::env::var("CODEWHALE_CLOUD_API_KEY").map_or(true, |k| k.is_empty()) {
anyhow::bail!("cloud API key not configured");
} Try / catch
match result {
Err(e) if e.to_string().contains("listing failed (HTTP 40") => warn_user("check cloud credentials"),
Err(e) if e.to_string().contains("listing failed") => retry_with_backoff(list, 2),
other => other,
} Prevention
- Keep the cloud API key configured and rotated before expiry.
- Treat listing as read-only and safe to retry with backoff.
- On 400/422, check whether the provider changed the label-filter contract and update the client.
- Monitor account quota so exhausted plans don't break reconciliation passes.
When it happens
Trigger: Any code path that enumerates labeled sandboxes when the provider list endpoint returns non-success: auth failure (401), rate limit (429), control-plane 5xx, or a request rejected because the label query payload is no longer accepted.
Common situations: Expired or missing API key; provider outage; quota exceeded on the account; provider API change altering the expected label-filter request format (leading to 400/422).
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- Codewhale account request failed
- Codewhale account request failed
- Codewhale account request failed
- Codewhale account request failed
- The Codewhale service returned HTTP
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/e9c71f174014e12f.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/cloud_dispatch.rs:1804
// The provider's list call takes a JSON-encoded exact-match labels
// filter (same OpenAPI family as create/get/delete above); filtering
// on the product tag keeps the response to Codewhale dispatch
// sandboxes only — never the user's own sandboxes on a shared key.
let mut url = Self::control_plane_url("sandbox")?;
url.query_pairs_mut().append_pair(
"labels",
&format!("{{\"{SANDBOX_PRODUCT_LABEL}\":\"{SANDBOX_PRODUCT_VALUE}\"}}"),
);
let response = Self::send_json(
reqwest::Method::GET,
&url,
&api_key,
serde_json::Value::Null,
)?;
let status = response.status();
let text = response.text().unwrap_or_default();
if !status.is_success() {
bail!("Cloud agent sandbox listing failed (HTTP {status}).");
}
let parsed: serde_json::Value = serde_json::from_str(&text)
.context("the cloud agent service returned an unreadable sandbox list")?;
let rows = parsed.as_array().cloned().unwrap_or_default();
let mut sandboxes = Vec::new();
for row in rows {
let sandbox_id = row
.get("id")
.and_then(serde_json::Value::as_str)
.unwrap_or("")
.trim()
.to_string();
if !valid_sandbox_id(&sandbox_id) {
continue;
}
let job_id = row
.get("labels")
.and_then(|labels| labels.get(SANDBOX_JOB_LABEL))View on GitHub (pinned to 73e0f67d83)