-
-
Notifications
You must be signed in to change notification settings - Fork 80.7k
[Bug]: truncateCloseReason can exceed its byte cap and emit U+FFFD mojibake when cutting mid-UTF-8 sequence #99976
Copy link
Copy link
Closed
Labels
P2Normal backlog priority with limited blast radius.Normal backlog priority with limited blast radius.clawsweeper:current-main-reproClawSweeper found a high-confidence current-main issue reproduction.ClawSweeper found a high-confidence current-main issue reproduction.clawsweeper:fix-shape-clearClawSweeper found a clear likely implementation shape for this issue.ClawSweeper found a clear likely implementation shape for this issue.clawsweeper:queueable-fixClawSweeper marked this issue as an existing queue_fix_pr work candidate.ClawSweeper marked this issue as an existing queue_fix_pr work candidate.impact:ux-frictionUser-facing flow adds avoidable confusion or support burden without fully blocking progress.User-facing flow adds avoidable confusion or support burden without fully blocking progress.issue-rating: 🦀 challenger crabExceptional issue quality: high-confidence current-main reproduction and actionable evidence.Exceptional issue quality: high-confidence current-main reproduction and actionable evidence.maturity:stableIssue affects a taxonomy feature currently scored M4/M5.Issue affects a taxonomy feature currently scored M4/M5.no-staleExclude from stale automationExclude from stale automation
Description
Metadata
Metadata
Assignees
Labels
P2Normal backlog priority with limited blast radius.Normal backlog priority with limited blast radius.clawsweeper:current-main-reproClawSweeper found a high-confidence current-main issue reproduction.ClawSweeper found a high-confidence current-main issue reproduction.clawsweeper:fix-shape-clearClawSweeper found a clear likely implementation shape for this issue.ClawSweeper found a clear likely implementation shape for this issue.clawsweeper:queueable-fixClawSweeper marked this issue as an existing queue_fix_pr work candidate.ClawSweeper marked this issue as an existing queue_fix_pr work candidate.impact:ux-frictionUser-facing flow adds avoidable confusion or support burden without fully blocking progress.User-facing flow adds avoidable confusion or support burden without fully blocking progress.issue-rating: 🦀 challenger crabExceptional issue quality: high-confidence current-main reproduction and actionable evidence.Exceptional issue quality: high-confidence current-main reproduction and actionable evidence.maturity:stableIssue affects a taxonomy feature currently scored M4/M5.Issue affects a taxonomy feature currently scored M4/M5.no-staleExclude from stale automationExclude from stale automation
Type
Fields
Priority
None yet
What happened
truncateCloseReasontruncates WebSocket close reasons at the raw byte level (Buffer.subarray), which can cut a multi-byte UTF-8 sequence in half. Node then decodes the dangling partial sequence as U+FFFD replacement characters, so the returned string:�) at the end of the close reason shown to clients, andmaxBytescap the function is supposed to enforce, because each U+FFFD is 3 bytes.Code:
openclaw/src/gateway/server/close-reason.ts
Lines 10 to 19 in 4287d26
Repro
Expected
Output stays within
maxByteswhen re-encoded and never ends in a replacement character; the partial code point should be dropped instead (e.g. back up over UTF-8 continuation bytes before slicing, or decode withTextDecoderin streaming mode).Impact
CLOSE_REASON_MAX_BYTES = 120keeps default calls under the RFC 6455 123-byte limit only by luck of the 3-byte margin; a caller passingmaxBytesof 122–123 can produce a reasonwsrejects withRangeError).Environment
Current
main(4287d26).Found via source review (AI-assisted).