Skip to content

kitty graphics passthrough: large APC bursts stall the parser or silently drop payload segments (measured on 0.14.2) #431

Description

@tobias-hui

Summary

Measured on luvus 0.14.2 (pane under luvus --session, host terminal Ghostty 1.3.1 on Linux/Hyprland): the kitty-graphics APC passthrough path — the one #324 is building — forwards small APCs inline at the correct cell, but corrupts or stalls once in-band image traffic grows past a few hundred KB. Two distinct failure modes, both reproduced with the self-contained script below.

This is real-world validation data for the #324 path: whatever currently forwards APCs needs backpressure/drain handling before wider rollout.

Bug 1 — one large write into the pane pty stalls the passthrough parser

A child writing a single contiguous ~1.4 MB burst (a complete chunked kitty transmit + delete + placement, as one terminal-cell update) stalls forwarding partway: the outer-terminal recording stops mid-APC ~19 KB into the burst. Bytes that follow are then forwarded at whatever cursor luvus has drifted to — observed at its own bottom chrome row — because a real kitty placement (a=p) anchors at forward-time cursor. Symptom: the image appears at the bottom of the screen, on top of the user's input, and persists (placements are terminal-side state the multiplexer never repaints).

Bug 2 — unpaced chunked APC streams silently drop payload segments

The same ~1.4 MB transmit written as 342 separate ≤4 KiB APC chunk writes (kitty's 4096-byte payload limit), with no delay, loses contiguous base64 runs of ~2.5-10 KB around the 350-425 KB mark (one run starts exactly at 98304 = 96 KiB, suggesting a fixed accumulation buffer). The image data after the gap is corrupt (PNG no longer decodes). Pacing each chunk write 5 ms apart forwards 1.4 MB fully intact; 2 ms per chunk still loses data.

Repro

https://gist.github.com/tobias-hui/24a090640351d8ad87b1c8b019a030d3 — self-contained bash, generates its own probe PNG, three modes: giant, chunked, paced. Run inside a luvus pane while recording the outer-terminal byte stream (run the luvus TUI under a pty recorder, e.g. script -qec "luvus --session repro" capture.bin, or equivalent). Verify by extracting every \x1b_G...;<base64>\x1b\\ APC payload from the recording, reassembling, and comparing against the transmitted base64 / decoding the PNG.

Small APCs (single-chunk transmits, a=p placements, U=1 keys) forward verbatim and correctly anchored — the failure mode is volume, not key handling.

Environment

  • luvus 0.14.2, server + TUI client same host, LUVUS_ENV=1 in pane
  • Host terminal: Ghostty 1.3.1-arch2, TERM=xterm-256color, Linux x86_64 Hyprland (Wayland)

Workaround (client side)

We pace chunk writes ≥5 ms apart and keep large transmits out of single cell writes in jcode (commit 89e0535f); noting it here in case it helps others until the passthrough handles backpressure.

Activity

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

    status: needs triageNeeds maintainer review and classification

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions