Skip to content

bug: lazygit writes OSC 4 palette escapes to stdout under GUI frontends (Neovide), corrupting the RPC stream #2921

Description

@wmaurer

Did you check docs and existing issues?

  • I have read all the snacks.nvim docs
  • I have updated the plugin to the latest version before submitting this issue
  • I have searched the existing issues of snacks.nvim
  • I have searched the existing issues of plugins related to this issue

Neovim version (nvim -v)

0.12.4

Operating system/version

Manjaro Linux (kernel 6.12.96)

Describe the bug

Snacks.lazygit sets terminal palette entries for numeric theme keys by writing OSC 4 escape sequences directly to stdout:

-- lua/snacks/lazygit.lua:153
pcall(io.write, ("\27]4;%d;%s\7"):format(k, color[1]))

This is only valid when the builtin TUI is attached. With a GUI frontend (Neovide, neovim-qt, any --embed client), stdout is the msgpack-RPC channel, so the raw bytes go into the protocol stream instead of a terminal. Neovide logs this on every lazygit open:

ERROR [neovide::bridge::session] ]4;241;#65bcff

The pcall does not help here because the write itself succeeds; it just writes to the wrong place.

Neovim core guards its own escape writes with a check over nvim_list_uis() (ui.chan == 1 and ui.stdout_tty, see runtime/lua/vim/_defaults.lua), and since 0.12 it provides vim.api.nvim_ui_send() for exactly this purpose (routed to the TUI host terminal, no-op for GUIs; core's OSC 52 clipboard moved to it).

Note: under Neovide the attached UI reports chan == 1, stdout_tty == false, so stdout_tty is the field that discriminates; guess_handle(1) is not reliable because UIs can attach and detach over a session.

The same adapted code in AstroNvim/astroui had the same bug and was fixed in AstroNvim/astroui#66 with:

if vim.api.nvim_ui_send then -- 0.12+
  pcall(vim.api.nvim_ui_send, ("\27]4;%d;%s\7"):format(k, color[1]))
elseif tui_attached() then -- nvim_list_uis() scan: ui.chan == 1 and ui.stdout_tty
  pcall(io.write, ("\27]4;%d;%s\7"):format(k, color[1]))
end

Happy to send a PR with the equivalent change if you want it.

Steps To Reproduce

  1. Start Neovide from a terminal with neovide --no-fork using the repro below (forked Neovide discards stderr, so the error is invisible in a normal launch)
  2. Run :lua Snacks.lazygit()
  3. Check the terminal's stderr: ERROR [neovide::bridge::session] ]4;241;...

Expected Behavior

Palette escapes are only emitted when a TUI attached to a real terminal can act on them; under GUI frontends nothing is written to the RPC stream.

Repro

vim.env.LAZY_STDPATH = ".repro"
load(vim.fn.system("curl -s https://raw.githubusercontent.com/folke/lazy.nvim/main/bootstrap.lua"))()

require("lazy.minit").repro({
  spec = {
    { "folke/snacks.nvim", opts = { lazygit = { enabled = true } } },
  },
})

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