{"record":{"id":"f3ba82d9ed23c385","repo":"Hmbown/CodeWhale","slug":"runtime-api-returned-status-detail","errorCode":null,"errorMessage":"runtime API returned {status}: {detail}","messagePattern":"runtime API returned (.+?): (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/app-server/src/lib.rs","lineNumber":1111,"sourceCode":"    }\n\n    fn authed(&self, builder: reqwest::RequestBuilder) -> reqwest::RequestBuilder {\n        match self.auth_token.as_deref() {\n            Some(token) => builder.bearer_auth(token),\n            None => builder,\n        }\n    }\n\n    async fn request_json(&self, builder: reqwest::RequestBuilder) -> Result<Value> {\n        let response = builder.send().await?;\n        let status = response.status();\n        let body = response.text().await?;\n        if !status.is_success() {\n            let detail = body.trim();\n            if detail.is_empty() {\n                bail!(\"runtime API returned {status}\");\n            }\n            bail!(\"runtime API returned {status}: {detail}\");\n        }\n        serde_json::from_str(&body).with_context(|| format!(\"invalid runtime API json: {body}\"))\n    }\n\n    async fn ensure_runtime_thread(\n        &mut self,\n        stdio_thread_id: &str,\n        hint: Option<RuntimeThreadHint>,\n    ) -> Result<String> {\n        if let Some(runtime_thread_id) = self.thread_map.get(stdio_thread_id) {\n            return Ok(runtime_thread_id.clone());\n        }\n        let hint = hint.unwrap_or_default();\n        let runtime_thread_id = self\n            .create_runtime_thread(hint.model, hint.workspace)\n            .await?;\n        self.thread_map\n            .insert(stdio_thread_id.to_string(), runtime_thread_id.clone());","sourceCodeStart":1093,"sourceCodeEnd":1129,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/app-server/src/lib.rs#L1093-L1129","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: app-server pointed at a stale runtime\nlet runtime = AppServer::new(\"http://127.0.0.1:7000\"); // old process, routes moved\n\n// after: spawn/attach the matching runtime build and reuse its reported base_url\nlet runtime = AppServer::new(runtime_handle.base_url()); // same-version runtime","handlingStrategy":"try-catch","validationCode":"// Before constructing the app-server, probe the runtime cheaply\nasync fn runtime_healthy(base: &str) -> bool {\n    reqwest::get(format!(\"{base}/health\")).await\n        .map(|r| r.status().is_success())\n        .unwrap_or(false)\n}","typeGuard":null,"tryCatchPattern":"match server.request_json(builder).await {\n    Ok(v) => v,\n    Err(e) => {\n        let msg = e.to_string();\n        if msg.starts_with(\"runtime API returned\") {\n            // inspect status/detail; 401 -> re-auth, 404 -> rebuild thread map, 5xx -> surface to user\n            return recover_from_runtime_status(msg);\n        }\n        return Err(e);\n    }\n}","preventionTips":["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."],"tags":["http","app-server","runtime","status-code"],"backgroundTag":"http-error-response","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}