Hmbown/CodeWhale · error · anyhow::Error

runtime API returned {status}: {detail}

Error message

runtime API returned {status}: {detail}

What it means

The app-server's request_json helper reports any non-success HTTP status from the Codewhale runtime API. The runtime (TUI engine process) is expected to serve JSON endpoints; when it answers with 4xx/5xx, the trimmed body text is appended as detail, or omitted when empty. This is the generic transport-level failure for every runtime REST call the app-server makes.

Source

Thrown at crates/app-server/src/lib.rs:1111

    }

    fn authed(&self, builder: reqwest::RequestBuilder) -> reqwest::RequestBuilder {
        match self.auth_token.as_deref() {
            Some(token) => builder.bearer_auth(token),
            None => builder,
        }
    }

    async fn request_json(&self, builder: reqwest::RequestBuilder) -> Result<Value> {
        let response = builder.send().await?;
        let status = response.status();
        let body = response.text().await?;
        if !status.is_success() {
            let detail = body.trim();
            if detail.is_empty() {
                bail!("runtime API returned {status}");
            }
            bail!("runtime API returned {status}: {detail}");
        }
        serde_json::from_str(&body).with_context(|| format!("invalid runtime API json: {body}"))
    }

    async fn ensure_runtime_thread(
        &mut self,
        stdio_thread_id: &str,
        hint: Option<RuntimeThreadHint>,
    ) -> Result<String> {
        if let Some(runtime_thread_id) = self.thread_map.get(stdio_thread_id) {
            return Ok(runtime_thread_id.clone());
        }
        let hint = hint.unwrap_or_default();
        let runtime_thread_id = self
            .create_runtime_thread(hint.model, hint.workspace)
            .await?;
        self.thread_map
            .insert(stdio_thread_id.to_string(), runtime_thread_id.clone());

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Read the {status} and {detail} in the message: a 404 usually means the thread/turn id no longer exists in the runtime; 401 means the auth token is stale; 500 means the runtime itself failed.
  2. Verify the runtime process is alive and the base_url the app-server was constructed with actually points at the Codewhale runtime API.
  3. Check for version mismatch between the app-server crate and the runtime/TUI build; rebuild/reinstall both from the same release so routes and payloads agree.
  4. If detail is an HTML page from a proxy, remove the proxy or route the runtime traffic directly to the runtime port.

Example fix

// before: app-server pointed at a stale runtime
let runtime = AppServer::new("http://127.0.0.1:7000"); // old process, routes moved

// after: spawn/attach the matching runtime build and reuse its reported base_url
let runtime = AppServer::new(runtime_handle.base_url()); // same-version runtime
Defensive patterns

Strategy: try-catch

Validate before calling

// Before constructing the app-server, probe the runtime cheaply
async fn runtime_healthy(base: &str) -> bool {
    reqwest::get(format!("{base}/health")).await
        .map(|r| r.status().is_success())
        .unwrap_or(false)
}

Try / catch

match server.request_json(builder).await {
    Ok(v) => v,
    Err(e) => {
        let msg = e.to_string();
        if msg.starts_with("runtime API returned") {
            // inspect status/detail; 401 -> re-auth, 404 -> rebuild thread map, 5xx -> surface to user
            return recover_from_runtime_status(msg);
        }
        return Err(e);
    }
}

Prevention

When it happens

Trigger: Any runtime API request (thread lifecycle, turn start, etc.) that returns a non-2xx status: unknown/mistyped runtime thread id (404), auth token rejected (401), malformed request body (400), or a runtime internal error (500) with or without a body.

Common situations: Runtime process version skew (app-server newer than runtime, route removed/renamed), auth token expired or mismatched between processes, runtime crashed mid-request behind a proxy that returns an HTML error page (empty/plain-text detail), wrong base_url pointing at a non-runtime server.

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@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/f3ba82d9ed23c385. Report an issue: GitHub.