You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Kitty graphics protocol load and display doesn't replace existing image with provided ID #6711
Originally posted by chase March 12, 2025
Currently, from what I can tell, Ghostty will ignore the ID and allocate a new image ID instead of setting the image with that ID.
Sometimes, using an id is not appropriate, for example, if you do not want to replace a previously sent image with the same id, or if you are sending a dummy image and do not want it stored by the terminal emulator. In that case, you can use the query action, set a=q. Then the terminal emulator will try to load the image and respond with either OK or an error, as above, but it will not replace an existing image with the same id, nor will it store the image.
Or simply put, the logical opposite is: for transmission and display, it will replace an existing image with the same ID and store it.
I ran into this issue while implementing a GIF-animation fallback for a terminal Slack client. Since Ghostty doesn't handle kitty animation frames yet (#5255), I stream GIF frames by retransmitting image data under a fixed image id — which made me care a lot about the exact replacement semantics, so I traced them through the source (current main, also checked v1.3.1).
TL;DR: explicit-id replacement works as specified. I couldn't reproduce the behavior described in the OP at the source level, which also matches @chase's own follow-up ("Simply replacing 2 image IDs seems fine and works as expected").
One thing that can look like "replacement not working" from the client side: every a=T without an explicit p= creates a new internal placement (graphics_storage.zig L341-351 — intentionally, per the comment, and consistent with the protocol where each display command adds a placement). So a client that "replaces" an image by repeatedly sending a=T,i=<same-id> accumulates placements. The fix on the client side is to use a=t (transmit-only) for the data swap and keep the original placement — that works nicely against Ghostty today (it's what my GIF streaming ended up doing).
Given the OP's follow-up and the above, this issue can probably be closed — unless the eviction edge case (@chase's last comment) is still worth tracking separately. Hope this helps with the triage effort under #8272.
Activity
chase commentedon Mar 15, 2025
After looking at the code for a bit, it seems like this behavior is already intended to be implemented and should be working.
ghostty/src/terminal/kitty/graphics_exec.zig
Lines 334 to 342 in 644acda
I'll try to figure out a minimal case to reproduce the issue. I'm guessing it has to do with not tracking evictions, but nothing certain yet.
chase commentedon Mar 16, 2025
Simply replacing 2 image IDs seems fine and works as expected.
It does seem related to images being evicted, so maybe the case is that
image.id == 0after eviction?fix: resolve image.nvim persistence on Ghostty terminal
jun9898 commentedon Aug 10, 2026
I ran into this issue while implementing a GIF-animation fallback for a terminal Slack client. Since Ghostty doesn't handle kitty animation frames yet (#5255), I stream GIF frames by retransmitting image data under a fixed image id — which made me care a lot about the exact replacement semantics, so I traced them through the source (current
main, also checked v1.3.1).TL;DR: explicit-id replacement works as specified. I couldn't reproduce the behavior described in the OP at the source level, which also matches @chase's own follow-up ("Simply replacing 2 image IDs seems fine and works as expected").
Details:
graphics_exec.zig(if (loading.image.id == 0)). An expliciti=is preserved.graphics_storage.zig(getOrPutAssumeCapacity(img.id)) frees the previous image and overwrites it.One thing that can look like "replacement not working" from the client side: every
a=Twithout an explicitp=creates a new internal placement (graphics_storage.zigL341-351 — intentionally, per the comment, and consistent with the protocol where each display command adds a placement). So a client that "replaces" an image by repeatedly sendinga=T,i=<same-id>accumulates placements. The fix on the client side is to usea=t(transmit-only) for the data swap and keep the original placement — that works nicely against Ghostty today (it's what my GIF streaming ended up doing).Given the OP's follow-up and the above, this issue can probably be closed — unless the eviction edge case (@chase's last comment) is still worth tracking separately. Hope this helps with the triage effort under #8272.
mitchellh commentedon Aug 20, 2026
This is fixed on main (to be 1.4) now, via multiple issues: #13173, then #13723, and #13893