Hmbown/CodeWhale · error · MachineError

The Codewhale service returned HTTP

Error message

The Codewhale service returned HTTP {status}: {server_message}

What it means

After a machine-token creation request, the CLI classifies any non-2xx HTTP response via classify(&response) and returns it as an error. The message template "The Codewhale service returned HTTP {status}: {server_message}" is the server-classified error shape: the request reached the service but was rejected (e.g. auth failure, validation, rate limit).

Solutions

  1. Re-authenticate so the request carries a valid bearer token
  2. Check the returned server_message and status for the specific cause (validation, rate limit, auth)
  3. Retry after the indicated retry_after delay for 429 responses
  4. Retry later or check service status for 5xx errors

Example fix

// before
let created: ApiKeyCreateResponse = decode_json(response)?;
// after (defensive check at call site)
if !(200..300).contains(&response.status) {
    eprintln!("key creation failed: HTTP {} — check session token and key name", response.status);
    return Err(anyhow::Error::new(classify(&response)));
}
Defensive patterns

Strategy: try-catch

Validate before calling

if !(200..300).contains(&response.status) {
    return Err(anyhow!("HTTP {}: {}", response.status, response.retry_after.map(|d| d.to_string()).unwrap_or_default()));
}

Try / catch

match create_api_key(...) {
    Ok(created) => write_created_key(out, &created)?,
    Err(e) if e.to_string().contains("returned HTTP") => {
        if e.to_string().contains("429") { sleep_with_backoff(); retry(); }
        else if e.to_string().contains("401")||e.to_string().contains("403") { reauthenticate()?; }
        else { return Err(e); }
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: `codewhale account api-keys create` where the response status is outside 200..300: expired/invalid machine session bearer, 422 validation of the key name, 429 rate limit, or 5xx server error.

Common situations: Session token expired before creating a key; duplicate or invalid API key name; hitting creation rate limits; service incident returning 5xx.

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/c389da02071e0bea. Report an issue: GitHub.

Appendix: source

Thrown at crates/cli/src/cloud/machine.rs:916

            let body = serde_json::to_vec(&ApiKeyCreateRequest {
                name,
                expires_in_days: create.expires_in_days,
                scopes,
            })
            .context("failed to encode the Codewhale API key request")?;
            // `Retry::Never` is the whole point of the enum here: a POST that
            // actually succeeded server-side would mint a second key whose
            // one-time secret the caller never saw, and therefore can never
            // revoke by id from the output they hold.
            let response = client.execute_authenticated_with_retry(
                HttpMethod::Post,
                "/api/account/api-keys",
                Some(body),
                Retry::Never,
                sleeper,
            )?;
            if !(200..300).contains(&response.status) {
                return Err(anyhow::Error::new(classify(&response)));
            }
            let created: ApiKeyCreateResponse = decode_json(response)?;
            write_created_key(out, &created)?;
            if create.use_locally {
                save_key_as_local_codewhale_credential(provider_secrets, &created.secret, out)?;
            }
            Ok(())
        }
        ApiKeysCommand::List => {
            let response = client.execute_authenticated_with_retry(
                HttpMethod::Get,
                "/api/account/api-keys",
                None,
                Retry::Idempotent,
                sleeper,
            )?;
            if !(200..300).contains(&response.status) {
                return Err(anyhow::Error::new(classify(&response)));

View on GitHub (pinned to 73e0f67d83)