Hmbown/CodeWhale · error · anyhow::Error

runtime event stream ended before turn.completed

Error message

runtime event stream ended before turn.completed

What it means

The app-server drains runtime SSE events waiting for a turn.completed (or terminal-status) event. If the HTTP stream ends (server closes connection, chunk iterator returns None) before any terminal event arrives, this bail fires. The turn's outcome is genuinely unknown — it neither completed nor reported an error.

Source

Thrown at crates/app-server/src/lib.rs:1333

                                }),
                            )
                            .await?;
                        }
                    }
                    "turn.completed" => {
                        let status = turn_terminal_status(&payload);
                        let error = payload
                            .pointer("/turn/error")
                            .and_then(Value::as_str)
                            .map(str::to_string);
                        return Ok((last_seq, status, error));
                    }
                    _ => {}
                }
            }
        }

        bail!("runtime event stream ended before turn.completed")
    }

    #[cfg(test)]
    fn from_base_url_for_test(base_url: String) -> Self {
        install_rustls_crypto_provider();
        Self {
            base_url,
            client: codewhale_release::platform_http_client_builder()
                .timeout(Duration::from_secs(5))
                .build()
                .expect("build reqwest test client"),
            auth_token: None,
            child: None,
            thread_map: HashMap::new(),
            last_seq_by_thread: HashMap::new(),
        }
    }
}

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Check whether the runtime process is still running and inspect its logs for a crash or panic during the turn.
  2. Retry the turn: because the stream ended without a terminal event, the previous turn id is not reusable; start a fresh turn.
  3. If a proxy sits in the middle, raise its read/idle timeout so long turns do not have their SSE connection closed.
  4. On resume, pass a since_seq older than the terminal event you need so turn.completed is re-delivered, or re-query turn state via the REST API instead of the stream.

Example fix

// before: assuming the stream always yields turn.completed
let (seq, status, err) = server.drain_turn_events(...).await?;

// after: treat a premature end as retryable
match server.drain_turn_events(...).await {
    Ok(outcome) => outcome,
    Err(e) if e.to_string().contains("before turn.completed") => {
        let state = server.query_turn_state(&turn_id).await?; // REST fallback
        state
    }
    Err(e) => return Err(e),
}
Defensive patterns

Strategy: retry

Validate before calling

// Prefer REST state over blind streaming when resuming
if let Some(state) = server.try_query_turn(&turn_id).await? {
    if state.is_terminal() { return Ok(state); }
}

Try / catch

let mut attempt = 0;
loop {
    match server.drain_turn_events(&turn_id, since).await {
        Ok(outcome) => break Ok(outcome),
        Err(e) if e.to_string().contains("before turn.completed") && attempt < 2 => {
            attempt += 1;
            since = last_known_seq;          // replay from what we saw
            continue;                        // stream was cut, turn may still be live
        }
        Err(e) => break Err(e),
    }
}

Prevention

When it happens

Trigger: Runtime process exits or restarts mid-turn; network drop between app-server and runtime closes the response body; runtime closes the SSE connection after keep-alive idle timeout without sending turn.completed; a turn that errors out without emitting a terminal event.

Common situations: Runtime crashed or was killed while a turn was in flight, proxy idle-timeout cutting long-running streams, runtime version that forgets to emit turn.completed on early error paths, or resuming from a since_seq where the terminal event was already emitted before subscription.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/2cd399cbaaad48cd. Report an issue: GitHub.