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
- 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.
- Verify the runtime process is alive and the base_url the app-server was constructed with actually points at the Codewhale runtime API.
- 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.
- 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
- Keep app-server and runtime built from the same release so route paths and auth agree.
- Health-check the runtime base_url before issuing turns.
- Log the status and detail verbatim once; do not retry 4xx blindly.
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
- MCP SSE rejected (transport=http url={} status={}): {}
- MCP SSE POST rejected (transport=sse endpoint={} status={}):
- unit must be 'chars' or 'lines'
- runtime SSE frame exceeded {MAX_SSE_FRAME_BYTES} bytes witho
- runtime event stream ended before turn.completed
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/f3ba82d9ed23c385.
Report an issue: GitHub.