Hmbown/CodeWhale · error · anyhow::Error

fork_at_user_message: depth {} exceeds {} user turn(s)

Error message

fork_at_user_message: depth {} exceeds {} user turn(s)

What it means

fork_at_user_message counted user messages walking the thread's turns from newest to oldest and the requested depth exceeds how many exist. Depth is tail-relative (1 = most recent user message), so this means the caller asked to fork at a user turn further back than the thread contains.

Source

Thrown at crates/tui/src/runtime_threads.rs:5051

        depth_from_tail: usize,
    ) -> Result<(ThreadRecord, Option<String>)> {
        let source = self.get_thread(id).await?;
        let source_turns = self.store.list_turns_for_thread(&source.id)?;

        // Walk turns from newest to oldest. For each turn, ask: does it
        // contain a UserMessage item? If yes, it counts toward the depth.
        let mut user_turn_indices: Vec<usize> = Vec::new();
        for (idx, turn) in source_turns.iter().enumerate().rev() {
            let items = self.store.list_items_for_turn(&turn.id)?;
            if items
                .iter()
                .any(|item| item.kind == TurnItemKind::UserMessage)
            {
                user_turn_indices.push(idx);
            }
        }
        if depth_from_tail >= user_turn_indices.len() {
            bail!(
                "fork_at_user_message: depth {} exceeds {} user turn(s)",
                depth_from_tail,
                user_turn_indices.len()
            );
        }
        // `user_turn_indices` is newest-first because we iterated in
        // reverse, so the Nth element is exactly the Nth-from-tail user
        // turn in the original chronological list.
        let target_turn_idx = user_turn_indices[depth_from_tail];
        let target_turn_id = source_turns[target_turn_idx].id.clone();

        // Pull the original user-message text out of the dropped turn so
        // the caller can drop it back into the composer.
        let target_items = self.store.list_items_for_turn(&target_turn_id)?;
        let original_user_text = target_items
            .iter()
            .find(|item| item.kind == TurnItemKind::UserMessage)
            .and_then(|item| item.detail.clone());

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Count the user turns in the thread
  2. Request a depth within that count
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at crates/tui/src/runtime_threads.rs:5051 when the library encounters an invalid state.

Common situations: See trigger scenarios.


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