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

  1. Check the embedded HTTP status: 401/403 → refresh the cloud API key; 429 → back off and retry; 5xx → retry after the provider recovers.
  2. Retry the listing — it is a read-only call and safe to repeat.
  3. 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.
  4. 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

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


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)