[BUG] OSC 8 hyperlinks in custom statusline broken inside tmux — rendering layer analysis
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
OSC 8 hyperlinks from custom statusline programs don't render as clickable links when Claude Code runs inside tmux. This is a re-report of #23438 (closed as stale/locked), now with root cause analysis.
Related issues: #21586, #19976, #14011
Root Cause: Claude Code renders its statusline output outside Ink's React component tree, writing directly to the terminal. Ink's rendering cycle erases and rewrites all managed terminal lines on every update. This destroys any OSC 8 hyperlinks that were written directly to stdout by external statusline programs.
Ink itself is not the problem. As of slice-ansi v8 and @alcalzone/ansi-tokenize v0.3+, Ink's character grid pipeline fully supports OSC 8 round-tripping. Hyperlinks that flow through the Ink React tree survive rendering. The issue is that statusline content bypasses this tree entirely.
Rendering pipeline walkthrough:
- External statusline program writes OSC 8 hyperlinks to stdout
- Ink's next render cycle calls
eraseLines()to clear previous output - Ink rewrites its grid-rendered content (which doesn't include the external hyperlinks)
- The hyperlink text appears but is no longer clickable — the OSC 8 sequences were erased
What Should Happen?
OSC 8 escape sequences from statusline programs should pass through Claude Code's rendering layer unchanged and render as clickable hyperlinks inside tmux, just as they do outside tmux.
Suggested fix: Route statusline content through the Ink component tree rather than writing it directly to the terminal:
- Render statusline output inside an Ink
<Text>node (preserving raw OSC 8 sequences) - Use Ink's
<Link>component for statusline hyperlinks - Ensure the statusline rendering region is excluded from Ink's
eraseLines()scope
This is a Claude Code change only — no upstream Ink changes are needed.
Error Messages/Logs
No error messages. The statusline text renders visually, but hyperlinks are not clickable. Verified with `xxd` that the statusline binary outputs correct OSC 8 escape sequences — they are simply erased by Ink's render cycle.
Evidence that tmux is not the issue:
- `printf '\033]8;;https://example.com\033\\Click\033]8;;\033\\\n'` in the same tmux pane renders as a clickable link
- Same statusline works when running Claude Code outside tmux
Steps to Reproduce
- Configure a custom statusline that outputs OSC 8 hyperlinks:
````
\033]8;;https://example.com\033\\link text\033]8;;\033\\
- Start tmux with
allow-passthrough onand hyperlinks enabled interminal-features - Run Claude Code inside tmux
- Observe that statusline hyperlinks are not clickable
Control tests (both pass):
- Run
printf '\033]8;;https://example.com\033\\Click\033]8;;\033\\\n'in the same tmux pane → clickable - Run Claude Code outside tmux with the same statusline → clickable
Environment: Claude Code 2.1.x, tmux 3.5a, Ghostty terminal, macOS
Claude Model
Opus
Is this a regression?
Yes, this worked in a previous version
Last Working Version
_No response_
Claude Code Version
2.1.73
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
iTerm2
Additional Information
_No response_
This issue has 7 comments on GitHub. Read the full discussion on GitHub ↗