Hmbown/CodeWhale · error

SSE stream idle timeout after {}s — no data received (bytes_

Error message

SSE stream idle timeout after {}s — no data received (bytes_received={}, stream_age_ms={}, ms_since_last_chunk={})

What it means

On the Antigravity cloud-code route, the tokio timeout around the next chunk elapsed: no bytes arrived within the idle window. The message is built by the shared `stream_entry::idle_timeout_message` and reports idle seconds, bytes received, stream age, and time since the last chunk, distinguishing never-started from stalled-mid-stream.

Source

Thrown at crates/tui/src/client/cloud_code.rs:184

        let byte_stream = response.bytes_stream();
        let stream = async_stream::stream! {
            let mut buffer: Vec<u8> = Vec::new();
            let stream_start = std::time::Instant::now();
            let mut last_chunk_at = std::time::Instant::now();
            let mut bytes_received: usize = 0;
            let mut started = false;
            tokio::pin!(byte_stream);

            loop {
                let chunk = match tokio::time::timeout(stream_idle_timeout, byte_stream.next()).await {
                    Ok(Some(Ok(chunk))) => chunk,
                    Ok(Some(Err(e))) => {
                        yield Err(anyhow::anyhow!("Stream read error: {e}"));
                        return;
                    }
                    Ok(None) => break,
                    Err(_) => {
                        yield Err(anyhow::anyhow!(stream_entry::idle_timeout_message(
                            stream_idle_timeout,
                            bytes_received,
                            stream_start.elapsed(),
                            last_chunk_at.elapsed(),
                        )));
                        return;
                    }
                };
                bytes_received += chunk.len();
                last_chunk_at = std::time::Instant::now();
                buffer.extend_from_slice(&chunk);

                loop {
                    let line = match super::take_sse_line(&mut buffer) {
                        Ok(Some(line)) => line,
                        Ok(None) => break,
                        Err(err) => {
                            yield Err(anyhow::anyhow!("{err}"));

View on GitHub (pinned to 8880682c63)

Solutions

  1. Retry — silent stalls are usually transient
  2. Raise the stream idle timeout above the route's expected time-to-first-event
  3. If bytes_received=0 always, verify the endpoint/auth — the server never starts
  4. Bypass intermediaries that idle-kill quiet connections
Defensive patterns

Strategy: retry

Type guard

fn is_idle_timeout(err: &anyhow::Error) -> bool {
    err.to_string().contains("idle timeout after")
}

Try / catch

match stream.next().await {
    Some(Err(e)) if is_idle_timeout(&e) => {
        backoff().await;
        stream = client.reopen_cloud_code_stream(request).await?;
    }
    Some(Err(e)) => return Err(e),
    other => { /* forward */ }
}

Prevention

When it happens

Trigger: Long server-side thinking phase before the first cloud-code event; stalled proxy; provider hang after 200 OK; idle timeout smaller than the route's time-to-first-byte.

Common situations: Heavy agentic tasks with long silent planning phases; mobile/high-latency networks; proxies idle-killing quiet SSE connections.

Understand the failure class

Related errors


AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16). Data as JSON: /api/errors/4919144a674b6859. Report an issue: GitHub.