October 7, 2025·4 min read·engineeringfishterminalscompletion

Fish terminal negotiation and completion repairs

Fish 4.1.2 repairs remote completion, delayed Escape handling, prompt placement, and generated configuration after the 4.1 release.

An Escape key can reach the shell immediately and still take too long to change editing modes. That distinction matters in the Zellij regression fixed by fish 4.1.2. The report initially looked like a multiplexer delaying input. Reader logs showed fish receiving the byte, then waiting while it resolved an escape sequence. The delay happened inside interpretation of input that had already arrived.

The October 7 release notes describe several regressions identified in 4.1.0. Their common subject is integration with the surrounding terminal environment. Completion depended on a renamed helper. Prompt rendering mishandled focus events. The web configuration tool emitted an obsolete representation of an underline option. Each problem crossed a boundary between components that otherwise worked.

I like fish because its interactive defaults remove configuration work. That preference makes these repairs consequential. Completion and editing belong to the ordinary path through the shell, so a regression there affects the reason to choose it even when command execution remains correct.

Escape handling depends on protocol agreement

In discussion 11868, the reporter used vi bindings and observed a delay when leaving insert mode inside Zellij. The same shell outside Zellij did not show the problem. Changing fish_escape_delay_ms did not resolve it. Subsequent reader logs narrowed the failure further by showing the Escape byte arriving before the pause.

The workaround commit explains the mismatch. Fish permits a longer timeout while the Kitty keyboard protocol is enabled because a raw Escape byte should rarely need interpretation as a standalone key in that mode. Older Zellij versions failed to enable that protocol correctly and continued sending raw bytes. Fish applied an assumption about the negotiated protocol that the actual input did not satisfy.

The workaround recognizes affected Zellij versions and selects a different protocol path. This is a compatibility decision about terminal capabilities. Reducing a general timing variable would leave the disagreement about encoding unresolved. A shell needs to know what input representation its immediate terminal connection actually provides, including when that connection passes through a multiplexer.

The distinction also explains why reproductions need the terminal stack. The name of the physical terminal application alone does not describe everything between a keystroke and the shell. A multiplexer participates in the protocol, and its version can change which assumptions are valid.

Completion runs helper code before the command

The remote-path completion failure had a smaller implementation cause. Issue 11860 reported an unknown __fish_mktemp command on the first remote completion in a new shell. The resulting output also included scp usage and a complaint from rm. Subsequent completion attempts worked, which made the symptom depend on session state.

The fix changes the helper call to __fish_mktemp_relative. The surrounding code creates a temporary file while determining how to complete a remote target. It caches the result in __fish_scp_sftp, so that discovery does not repeat on every invocation. The reported first-use behavior follows the branch that performs this initialization.

This also explains why errors appeared before the user executed an scp command. Remote completion itself invokes supporting commands. Its temporary-file setup failed, and later operations received unsuitable inputs. Several printed errors therefore came from one incorrect helper name. They were not independent evidence of broken SSH configuration or a remote filesystem problem.

I value completion that ships with the shell because it makes command discovery available without assembling plugins. This bug illustrates the maintenance obligation that accompanies that choice. Bundled completion functions are executable integration code, and changes to internal helpers must reach their callers.

Focus events and multiline geometry

The prompt defect involved a different kind of hidden state. Issue 11870 describes a multiline prompt becoming corrupted in tmux after returning to a pane or changing focus. The report combined a two-line prompt with focus events and vi bindings. Those details describe the reported reproduction, rather than requirements for every possible manifestation.

The release notes identify the repaired behavior as multiline prompts redrawing on the wrong line after focus events, which fish currently enables only in tmux. An otherwise correct prompt function can still produce misplaced output when the reader's redraw position is wrong. Editing the function's text does not address that distinction. The shell must preserve the relationship between its logical prompt and the terminal's cursor position when handling asynchronous events.

Generated configuration must match option types

The web configuration patch replaces a default underline value of False with None. Underline had ceased to be a boolean in that parser, and the old default could generate --underline=False. The patch also handles long options whose values follow an equals sign, such as --background=red.

Those changes connect configuration input and output. A graphical choice eventually becomes shell configuration, so both the parser's accepted forms and its representation of an absent value affect the result. Fish 4.1.2 repairs that conversion alongside the terminal fixes. The practical effect is that ordinary customization and interaction again use representations understood by the next component.