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

  1. Re-run the auth flow and leave the browser tab open until it shows 'MCP OAuth authentication finished. You can close this tab.'
  2. Retry `run_mcp_auth_flow`; the error is transient when caused by a premature tab close.
  3. Check that local loopback connections are not intercepted/reset by firewall, proxy, or antivirus software.
  4. 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

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


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)