Skip to content

mux: fix panic when CDP debug log truncates mid-UTF-8-character - #7462

Open
JRVector9 wants to merge 1 commit into
manaflow-ai:mainfrom
JRVector9:fix-mux-cdp-debug-log-utf8
Open

JRVector9 wants to merge 1 commit into
manaflow-ai:mainfrom
JRVector9:fix-mux-cdp-debug-log-utf8

Conversation

@JRVector9

@JRVector9 JRVector9 commented Jul 6, 2026 •

Copy link
Copy Markdown

handle_text in mux-cdp logs every inbound CDP message under CMUX_MUX_CDP_DEBUG:

eprintln!("cdp<- {}", &text[..text.len().min(300)]);

&text[..300] slices by byte offset. When the cutoff falls inside a multi-byte UTF-8 character it panics (byte index 300 is not a char boundary). CDP payloads carry arbitrary page text, so a Target.targetInfoChanged for a tab whose title crosses offset 300 with a non-ASCII character is enough to hit it. The panic happens on the detached mux-cdp-reader thread, so it's swallowed: the reader stops, pending call()s time out, and the session goes quiet with no error. It only triggers with the debug flag set — i.e. while debugging the browser.

Truncate on a char boundary instead. Two unit tests cover the helper; the multi-byte one panics against the old byte slice and passes with the fix.

Verified in mux/:

  • cargo test -p mux-cdp — 4 unit + 3 integration tests pass
  • cargo fmt --check — clean
  • cargo clippy -p mux-cdp --all-targets --locked -- -D warnings — clean

The change is confined to mux-cdp, which doesn't depend on ghostty, so I ran crate-scoped checks rather than the full --workspace build (that needs the ghostty submodule). The same slice is present in the open #7378.


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.


Summary by cubic

Prevented a panic in mux-cdp debug logging by truncating CDP messages on UTF‑8 character boundaries. This keeps the reader thread running when CMUX_MUX_CDP_DEBUG is set and avoids silent session stalls.

  • Bug Fixes
    • Replaced &text[..min(300)] with a safe truncate_for_log(text, 300) that never splits multi-byte characters.
    • Added two unit tests for multi-byte truncation and short input.

Written for commit 6470411. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Bug Fixes
    • Improved handling of logged CDP message text to avoid crashes when truncating content with non-ASCII characters.
    • Ensured longer log messages are safely shortened without splitting multi-byte characters.
  • Tests
    • Added coverage for text truncation behavior, including multibyte characters and short inputs.

handle_text logged inbound CDP messages with &text[..text.len().min(300)],
which panics when the 300-byte cutoff lands inside a multi-byte UTF-8
character (e.g. a non-ASCII tab title in a Target.targetInfoChanged event).
The panic lands on the detached mux-cdp-reader thread, so it is swallowed and
the CDP session silently stalls. Only reachable with CMUX_MUX_CDP_DEBUG set.

Truncate on a char boundary instead. Adds two unit tests for the helper.
@vercel

vercel Bot commented Jul 6, 2026

Copy link
Copy Markdown

@lollol-jr is attempting to deploy a commit to the Manaflow Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jul 6, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: f9aef3c7-facd-41ea-b342-23951d136017

📥 Commits

Reviewing files that changed from the base of the PR and between f0c38b8 and 6470411.

📒 Files selected for processing (1)
  • mux/crates/mux-cdp/src/client.rs

📝 Walkthrough

Walkthrough

Replaced unsafe raw byte slicing used for truncating logged CDP text payloads with a new helper function that truncates on UTF-8 character boundaries, preventing panics on multibyte characters. Added unit tests covering multibyte boundary handling and short-input behavior.

Changes

Truncation Fix and Tests

Layer / File(s) Summary
Safe truncation helper
mux/crates/mux-cdp/src/client.rs
Adds truncate_for_log(text, max) to truncate strings at UTF-8 character boundaries and updates handle_text's debug logging to use it instead of raw byte slicing.
Unit tests
mux/crates/mux-cdp/src/client.rs
Adds tests verifying multibyte characters aren't split at the cutoff and that short strings remain unchanged.

Estimated code review effort: 1 (Trivial) | ~5 minutes

🚥 Pre-merge checks | ✅ 25
✅ Passed checks (25 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main fix: preventing a panic in mux-cdp debug log truncation at UTF-8 boundaries.
Description check ✅ Passed The description covers the summary and testing well, but omits the demo video, review trigger block, and checklist sections.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Cmux Swift Actor Isolation ✅ Passed Diff only touches mux/crates/mux-cdp/src/client.rs (Rust); no Swift files changed, so no Swift actor-isolation issues were introduced.
Cmux Swift Blocking Runtime ✅ Passed The PR touches only mux/crates/mux-cdp/src/client.rs; no Swift files or blocking runtime primitives were introduced.
Cmux Browser Automation Off-Main ✅ Passed Diff only changes mux-cdp CDP log truncation/tests; no browser.* socket commands, WebKit/AppKit routing, or policy-test coverage is touched.
Cmux Expensive Synchronous Load ✅ Passed PASS: The PR only changes Rust mux-cdp logging code; no Swift code or agent-history load paths are touched, so the Swift-specific rule doesn’t apply.
Cmux Cache Substitution Correctness ✅ Passed The patch only changes Rust CDP logging in mux-cdp; no Swift/TS/JS persistence/history/snapshot cache substitution is involved.
Cmux No Hacky Sleeps ✅ Passed PASS: The PR only changes Rust UTF-8 logging truncation and tests; no new sleeps, timers, polling, or wall-clock waits were added, and existing sleep calls were untouched.
Cmux Algorithmic Complexity ✅ Passed PASS: the change only adds a fixed 300-byte log truncation helper; the boundary scan is bounded to ≤3 byte checks and the rest is tests, with no scalable rescans.
Cmux Swift Concurrency ✅ Passed Diff only changes mux-cdp Rust code; no Swift files or async-pattern changes are introduced.
Cmux Swift @Concurrent ✅ Passed The diff only touches Rust (mux/crates/mux-cdp/src/client.rs); no Swift changes mean the Swift concurrent-annotation rule is inapplicable.
Cmux Swift File And Package Boundaries ✅ Passed No Swift files were changed; the PR only touches mux/crates/mux-cdp/src/client.rs, so the Swift package-boundary rule is not applicable.
Cmux Swiftpm Lockfiles ✅ Passed PR only changes mux/crates/mux-cdp/src/client.rs; no SwiftPM/Xcode/.gitignore/workflow/dependency files or Package.resolved diffs are involved.
Cmux Swift Logging ✅ Passed PASS: The PR only changes Rust mux-cdp debug logging; no Swift files or Swift logging patterns are added or changed, so the Swift rule is not applicable.
Cmux User-Facing Error Privacy ✅ Passed Only internal debug truncation changed; no user-facing error, alert, or API text was added or exposed.
Cmux Full Internationalization ✅ Passed Only change is a debug-only CDP log truncation helper plus tests in mux-cdp; no user-facing/localized text, web UI, or catalog files changed.
Cmux Swiftui State Layout ✅ Passed Diff only touches mux/crates/mux-cdp/src/client.rs (Rust); no SwiftUI views/state/layout patterns are introduced, so the rule doesn’t apply.
Cmux Architecture Rethink ✅ Passed PR only changes Rust mux-cdp log truncation and tests; no Swift UI lifecycle, timing, or shared-state architecture issues are introduced.
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PASS: The PR only changes mux/crates/mux-cdp/src/client.rs (Rust); no Swift/UI window code or auxiliary-window shortcut registration is touched, so the rule is not applicable.
Cmux Source Artifacts ✅ Passed Only mux/crates/mux-cdp/src/client.rs changed, and it contains source/test code allowed by the artifact rule.
Cmux No Test Or Debug Seam In Production Source ✅ Passed PR only changes mux/crates/mux-cdp/src/client.rs; no Swift production Sources/** file or debug/test seam is touched.
Cmux No Ambient Global State ✅ Passed PR is Rust-only; no Swift files changed, and the added helper is private. No new ambient-global-state API or singleton is introduced.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Fixes a panic in the mux-cdp-reader thread that occurred when the CDP debug log truncation sliced a UTF-8 string at a non-character boundary. The affected eprintln! line used a raw byte-offset slice (&text[..300]), which panics when a multi-byte character straddles offset 300 in CDP payloads containing non-ASCII text such as tab titles.

  • Introduces truncate_for_log(text, max), which walks the byte offset backward until it lands on a valid char boundary before slicing, and replaces the raw slice at the one call site.
  • Adds two focused unit tests: one that constructs a string whose 4-byte emoji straddles byte 300 and asserts the truncation drops the emoji rather than panicking, and one that verifies short inputs pass through unchanged.

Confidence Score: 5/5

The change is a one-file, debug-path-only fix with no impact on normal execution; the new helper is straightforward and the tests directly exercise the boundary that caused the panic.

The manual walk-back loop is correct and equivalent to the standard library's floor_char_boundary, the early-return fast path avoids any work on short strings, and the two tests pin both the panicking case and the trivial case. The fix is isolated to the CMUX_MUX_CDP_DEBUG logging branch, so production data flow is unchanged.

No files require special attention.

Important Files Changed

Filename Overview
mux/crates/mux-cdp/src/client.rs Adds truncate_for_log helper that walks back from the byte limit to a valid char boundary, replaces the panicking raw byte slice in the debug log path, and adds two unit tests covering the multi-byte and short-input cases.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["handle_text(inner, text)"] --> B{"cdp_debug?"}
    B -->|No| D["parse JSON / dispatch event"]
    B -->|Yes| C["truncate_for_log(text, 300)"]
    C --> E{"text.len() <= max?"}
    E -->|Yes| F["return text unchanged"]
    E -->|No| G["end = max, walk back until is_char_boundary(end)"]
    G --> H["return text slice to end"]
    F --> I["eprintln cdp message"]
    H --> I
    I --> D
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A["handle_text(inner, text)"] --> B{"cdp_debug?"}
    B -->|No| D["parse JSON / dispatch event"]
    B -->|Yes| C["truncate_for_log(text, 300)"]
    C --> E{"text.len() <= max?"}
    E -->|Yes| F["return text unchanged"]
    E -->|No| G["end = max, walk back until is_char_boundary(end)"]
    G --> H["return text slice to end"]
    F --> I["eprintln cdp message"]
    H --> I
    I --> D
Loading

Reviews (1): Last reviewed commit: "mux: fix panic when CDP debug log trunca..." | Re-trigger Greptile

Comment on lines +382 to +391
fn truncate_for_log(text: &str, max: usize) -> &str {
if text.len() <= max {
return text;
}
let mut end = max;
while end > 0 && !text.is_char_boundary(end) {
end -= 1;
}
&text[..end]
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The manual walk-back loop is correct, but Rust 1.72 stabilised str::floor_char_boundary which does exactly this and avoids the custom loop. If the crate's MSRV is 1.72 or later, the whole body collapses to one line and the intent is immediately obvious from the standard-library name.

Suggested change
fn truncate_for_log(text: &str, max: usize) -> &str {
if text.len() <= max {
return text;
}
let mut end = max;
while end > 0 && !text.is_char_boundary(end) {
end -= 1;
}
&text[..end]
}
fn truncate_for_log(text: &str, max: usize) -> &str {
&text[..text.floor_char_boundary(max.min(text.len()))]
}

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@teamleaderleo teamleaderleo added ready-to-land Reviewed and ready to land when CI is green area: browser The embedded browser, web surfaces, inline VS Code S2: major A crash, hang, lost state, broken connection, or a regression on a path people use labels Sep 30, 2026
@teamleaderleo

Copy link
Copy Markdown
Collaborator

The UTF-8 truncation bug is still present in the current cmux-tui CDP client, but the old mux/ path was rebranded in #7710, so this needs a small port before landing.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: browser The embedded browser, web surfaces, inline VS Code ready-to-land Reviewed and ready to land when CI is green S2: major A crash, hang, lost state, broken connection, or a regression on a path people use

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants