Kuberwastaken/claurst · error · anyhow::Error
models endpoint returned
Error message
models endpoint returned {} What it means
In `wait_for_authorization_code` (src-rust/crates/mcp/src/oauth.rs:275), after the MCP OAuth callback request arrives on the local listener, the server writes a fixed HTTP 200 response back to the browser before reading the redirect target. If `write_all` on the TCP stream fails, this anyhow error wraps the underlying io::Error. It means the local HTTP handshake with the browser could not complete.
Solutions
- Re-run the auth flow and leave the browser tab open until it shows 'MCP OAuth authentication finished. You can close this tab.'
- Retry `run_mcp_auth_flow`; the error is transient when caused by a premature tab close.
- Check that local loopback connections are not intercepted/reset by firewall, proxy, or antivirus software.
- If it reproduces consistently, verify the listener is still alive (no port hijack) and inspect the wrapped io::Error message for the root cause.
Example fix
// before: auth flow errors out when the user closes the tab too fast
// after: caller retries or tolerates the write failure by re-running the session
match run_mcp_auth_flow(&session).await {
Ok(result) => result,
Err(e) if e.to_string().contains("Failed to write OAuth callback response") => {
eprintln!("Browser closed too early, retrying...");
run_mcp_auth_flow(&session).await?
}
Err(e) => return Err(e),
} Defensive patterns
Strategy: try-catch
Try / catch
match run_mcp_auth_flow(&session).await {
Ok(r) => r,
Err(e) if e.to_string().contains("Failed to write OAuth callback response") => {
// transient: user closed the tab; retry the whole flow
run_mcp_auth_flow(&session).await?
}
Err(e) => return Err(e),
} Prevention
- Instruct users to keep the browser tab open until the confirmation page appears
- Avoid proxies/AV that intercept loopback traffic during auth
- Treat this error as retryable and automate one retry
When it happens
Trigger: The TCP writer to the browser (or HTTP client) errors during `writer.write_all(response.as_bytes()).await` after a callback request was accepted — e.g. the client closed the connection before the response was written, or the OS socket failed.
Common situations: User closes the browser tab (or presses Escape / navigates away) the instant the redirect lands, so the socket is already closed; a browser or proxy that aborts the connection early; system-level socket exhaustion; firewall/AV software resetting local loopback connections.
Related errors
- Bridge register: server returned
- start_bridge: bridge is not active
- Token exchange failed
- Token exchange failed
- API key creation failed
AI-assisted analysis of Kuberwastaken/claurst@b0637c97ec (2026-09-10).
Data as JSON: /api/errors/4840beb6d952e85a.
Report an issue: GitHub.
Appendix: source
Thrown at src-rust/crates/api/src/lib.rs:915
req = req
.header(
"anthropic-beta",
claurst_core::oauth_config::OAUTH_BETA_FLAGS.join(","),
)
.header(
"user-agent",
claurst_core::oauth_config::claude_code_user_agent(),
)
.header("x-app", "cli")
.header("Authorization", format!("Bearer {}", self.config.api_key));
} else {
req = req.header("x-api-key", &self.config.api_key);
}
let resp = req.send().await?;
if !resp.status().is_success() {
anyhow::bail!("models endpoint returned {}", resp.status());
}
#[derive(serde::Deserialize)]
struct ModelsResponse {
data: Vec<crate::AvailableModel>,
}
let body: ModelsResponse = resp.json().await?;
Ok(body.data)
}
// ---- Internal helpers --------------------------------------------
/// Build the common request and execute with retry logic.
async fn send_with_retry(
&self,
body: &Value,
) -> Result<wreq::Response, ClaudeError> {
View on GitHub (pinned to b0637c97ec)