Skip to content

[BUG] [v0.0.7] fork_session() copies one extra message into forked history due to off-by-one in index check #158

@katotomoya0105

Description

@katotomoya0105

Summary

In src/cortex-app-server/src/session_manager.rs, the fork_session() method copies the parent session's message history up to message_index into the new forked session. However, the loop guard uses i > message_index (strict greater-than) instead of i >= message_index, causing it to copy one extra message beyond the intended fork point.

Affected Code

// src/cortex-app-server/src/session_manager.rs  lines 590-598
if let Ok(history) = self.storage.read_history(session_id) {
    for (i, msg) in history.into_iter().enumerate() {
        if i > message_index {   // ← should be i >= message_index
            break;
        }
        if let Err(e) = self.storage.append_message(&new_session_id, &msg) {
            warn!("Failed to copy message to forked history: {}", e);
        }
    }
}

Root Cause

The intent of message_index is to fork the conversation at that message — i.e., the forked session should contain messages 0..message_index (exclusive). The condition i > message_index breaks only when i is strictly greater than message_index, meaning the message at index message_index itself is also copied. The correct guard is i >= message_index.

The same message_index is passed to Session::fork(config, conversation_id, message_index) on the cortex-core side (line 525), which presumably uses the index exclusively. The storage copy is therefore inconsistent with the core fork, leaving the forked session's persisted history one message longer than the in-memory conversation state.

Example

Given a history of 5 messages (indices 0–4) and message_index = 2:

Behaviour Messages copied
Current (buggy) 0, 1, 2 (3 messages)
Expected 0, 1 (2 messages)

Impact

  • The forked session's persisted history diverges from its actual in-memory conversation state.
  • When the forked session is later restored from storage, it will replay an extra message that was not part of the fork point, causing context confusion for the AI model.
  • The /ws ForkSession WebSocket command and the POST /cli/sessions/:id/fork HTTP endpoint are both affected.

Suggested Fix

if i >= message_index {
    break;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions