Skip to content

Regression: message navigation jumps to wrong lines after #490 (session tabs) #495

Closed

Description

@wzj-zz

Description

After updating opencode.nvim from 5e89ef9 to 6071fe7, the output-window message navigation actions (prev_message, next_message, prev_user_message, next_user_message) intermittently jump to the wrong place — the cursor lands on a wrong line/message instead of the message adjacent to the current position. Before the update navigation was always accurate.

I bisected by pinning: the problem is gone when pinned to b629972 (the commit immediately before #490) and present at 6071fe7 (#490 "Wip/session tabs"), so the session-tabs rework is the likely culprit.

Suspected cause

From reading the diff of #490, renderer.on_session_changed gained an early return:

function M.on_session_changed(_, new, old)
  if state.active_session_tab ~= rendered_session_tab then
    return
  end
  ...

If state.active_session_tab and the renderer-local rendered_session_tab get out of sync (e.g. session_tabs clears the active tab via store.set_raw('active_session_tab', nil), which does not fire the store subscription), the renderer skips re-rendering on session change. The output buffer and render_state line numbers then still describe the previous session while state.messages holds the new one, so navigation targets computed from render_state:get_message(id).line_start land on stale/wrong lines.

Possibly also related to the renderer context snapshot/restore (ctx:snapshot() / ctx:restore()) added in the same PR.

Steps to reproduce

Intermittent ("sometimes"). In my usage it shows up in longer sessions, seemingly more often after switching sessions via the session tree and scrolling up into older messages:

  1. Open the opencode windows with a long session (enough messages to trigger lazy rendering)
  2. Switch between sessions and/or scroll up into older messages
  3. Trigger prev_user_message / prev_message repeatedly
  4. Occasionally the cursor lands on a wrong line instead of the previous message

Environment

  • Neovim version: 0.12.2
  • OpenCode version: 1.18.29
  • opencode.nvim version or commit: broken at 6071fe7; works at b629972 and 5e89ef9
  • OS: Windows 11

Configuration

opencode.nvim configuration

Relevant keymaps (the behavior is also reproducible with the default [[ / [u mappings):

require('opencode').setup({
  keymap = {
    output_window = {
      ['<C-p>'] = { 'prev_message', desc = 'Prev message' },
      ['<M-p>'] = { 'prev_user_message', desc = 'Prev user message' },
    },
  },
})

Logs and screenshots

Happy to capture debug logs if you tell me what to enable.

Checklist

  • I have searched existing issues.
  • I have included all relevant details.
  • I have removed sensitive information from configs and logs.

Activity

  1. sudo-tee commented on Sep 17, 2026

    @sudo-tee
    Owner

    Thanks for all the info.

    I will have a look.

  2. wzj-zz commented on Sep 17, 2026

    @wzj-zz
    ContributorAuthor

    Update: root cause found — #490 is likely innocent, please disregard the regression claim

    I kept debugging this with runtime instrumentation and owe you a correction: the misattribution above. Two separate things were conflated:

    1. The dominant cause of my "wrong jumps": snacks.scroll (config-side, not opencode.nvim)

    I have snacks.scroll (smooth scrolling) enabled. It listens to WinScrolled and animates any topline change, so when opencode's jump actions set the cursor hundreds of lines away, snacks intercepted it and animated the cursor toward the target a few lines per frame — a ~570-line jump took seconds of visible crawling, landing mid-way on a wrong-looking position. That perfectly matched the "jumps are wrong / need a wait before they work" symptom, and it also churned on every streaming flush, which likely explains most of the perceived slowdown I blamed on #490.

    Fix on my side: disabling snacks.scroll entirely (scroll = { enabled = false }). Users who want to keep smooth scrolling can opt out just for the output buffer instead:

    vim.api.nvim_create_autocmd("FileType", {
      pattern = "opencode_output",
      callback = function(ev) vim.b[ev.buf].snacks_scroll = false end,
    })

    With this, I'm now running 6071fe7 (including #490) and navigation is instant and accurate. A short "known conflicts" note in the README (snacks.scroll / smooth-scroll plugins) might save other users the same hunt.

    2. A genuine renderer issue that remains: phantom entries in render_state

    While instrumenting I captured a real inconsistency, present already at b629972 (so older than #490): render_state can contain line-number entries for messages that have no rendered content in the buffer. In my capture, render_state held entries like user:1045 and assistant:1047 with line numbers, but the buffer had no header extmarks for those messages at all. Since next/prev_message navigation targets render_state:get_message(id).line_start, a jump can land on a line that corresponds to no rendered message; it self-heals once those messages actually render.

    Likely interaction: lazy rendering renders only the tail, while navigation walks the full ledger. Suggestion: navigation could skip messages whose rendered range doesn't exist on screen, or validate against actual extmarks.

  3. sudo-tee commented on Sep 18, 2026

    @sudo-tee
    Owner

    Thanks for the info I will check the issue with phanton render_state.

    Currrently there is major refactoring hapening for supporting opencode v2. I will try to sqeeze a fix to this. at the same time

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions