Hmbown/CodeWhale · error · anyhow::Error
Runtime event replay_limit cannot exceed {MAX_RUNTIME_EVENT_
Error message
Runtime event replay_limit cannot exceed {MAX_RUNTIME_EVENT_REPLAY_TAIL} What it means
replay_events bounds tail_limit by MAX_RUNTIME_EVENT_REPLAY_TAIL (4096, crates/tui/src/runtime_threads.rs:70) so a replay cannot materialize an unbounded event window; any request above the cap is rejected before the replay worker spawns (crates/tui/src/runtime_threads.rs:6532). Use the since_seq cursor for full history instead of a huge tail.
Source
Thrown at crates/tui/src/runtime_threads.rs:6532
thread_id: &str,
offset: u64,
limit: Option<usize>,
) -> Result<(Vec<RuntimeEventRecord>, u64)> {
let store = self.store.clone();
let thread_id = thread_id.to_string();
tokio::task::spawn_blocking(move || store.events_from_offset(&thread_id, offset, limit))
.await
.context("Runtime event cursor task failed")?
}
pub(crate) async fn replay_events(
&self,
thread_id: &str,
since_seq: Option<u64>,
tail_limit: Option<usize>,
) -> Result<RuntimeEventReplay> {
if tail_limit.is_some_and(|limit| limit > MAX_RUNTIME_EVENT_REPLAY_TAIL) {
bail!("Runtime event replay_limit cannot exceed {MAX_RUNTIME_EVENT_REPLAY_TAIL}");
}
let (base_tx, base_rx) = oneshot::channel();
let (batch_tx, batches) = mpsc::channel(2);
let store = self.store.clone();
let thread_id = thread_id.to_string();
tokio::task::spawn_blocking(move || {
store.publish_event_replay(&thread_id, since_seq, tail_limit, base_tx, batch_tx);
});
let base_seq = base_rx
.await
.context("Runtime event replay worker ended before initialization")?
.map_err(anyhow::Error::msg)?;
Ok(RuntimeEventReplay { base_seq, batches })
}
async fn ensure_engine_loaded(&self, thread_hint: &ThreadRecord) -> Result<EngineHandle> {
{
let mut active = self.active.lock().await;View on GitHub (pinned to 0c42157ee5)
Solutions
- Clamp tail_limit to 4096 (limit.min(MAX_RUNTIME_EVENT_REPLAY_TAIL)) before calling
- Page through history with since_seq cursors instead of one large tail
- Request only the window you actually render
- Compute limit from viewport size, not from total event count
Example fix
// before let replay = runtime.replay_events(&thread_id, None, Some(total_event_count)).await?; // after const MAX_TAIL: usize = 4096; let tail = Some(total_event_count.min(MAX_TAIL)); let replay = runtime.replay_events(&thread_id, since_seq, tail).await?;
Defensive patterns
Strategy: validation
Validate before calling
// Clamp before calling. const MAX_RUNTIME_EVENT_REPLAY_TAIL: usize = 4096; let tail_limit = tail_limit.map(|l| l.min(MAX_RUNTIME_EVENT_REPLAY_TAIL)); let replay = runtime.replay_events(thread_id, since_seq, tail_limit).await?;
Type guard
fn is_valid_tail_limit(limit: Option<usize>) -> bool {
limit.is_none_or(|l| l <= 4096)
} Try / catch
match runtime.replay_events(thread_id, since_seq, tail_limit).await {
Ok(replay) => { /* ... */ }
Err(err) if err.to_string().contains("replay_limit cannot exceed") => {
let clamped = tail_limit.map(|l| l.min(4096));
runtime.replay_events(thread_id, since_seq, clamped).await?;
}
Err(err) => return Err(err),
} Prevention
- Never derive replay_limit from total event counts; derive it from the viewport
- Page full history with the since_seq cursor instead of one large tail
- Centralize the 4096 cap as a shared constant clients import
- Add an integration test asserting oversized requests are clamped, not passed through
When it happens
Trigger: Passing tail_limit = Some(10_000) to replay_events; a client 'load entire history' button requesting all events as a tail; porting an older client that assumed unbounded limits; computing limit as total event count read from elsewhere.
Common situations: New UI features requesting big windows; scripts copying a store's event count into replay_limit; version upgrades that introduced the cap after clients were written.
Related errors
- context_window must be greater than 0
- custom provider '{provider_id}' must set [providers.{provide
- unknown field '{field_key}' for built-in provider '{provider
- unknown field '{field_key}' for custom provider '{provider_i
- persistent allow rules must be scoped to a workspace
AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20).
Data as JSON: /api/errors/3d66068e326d9b55.
Report an issue: GitHub.