Skip to content

fix(status): keep emoji / surrogate pairs intact during gateway status truncation#101609

Closed
maweibin wants to merge 1 commit into
openclaw:mainfrom
maweibin:fix/status-gateway-emoji-truncation
Closed

fix(status): keep emoji / surrogate pairs intact during gateway status truncation#101609
maweibin wants to merge 1 commit into
openclaw:mainfrom
maweibin:fix/status-gateway-emoji-truncation

Conversation

@maweibin

@maweibin maweibin commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

The shorten helper in src/commands/status-all/gateway.ts truncates gateway log-tail diagnostic messages with .slice() which counts UTF-16 code units. When a surrogate-pair character (emoji) straddles the cut boundary, .slice() produces a lone surrogate (e.g. \ud83d) that renders as in openclaw gateway status output.

This is user-visible: gateway log-tail summaries include provider error messages that can contain emoji or other supplementary characters.

Why This Change Was Made

Replace .slice() with truncateUtf16Safe() from @openclaw/normalization-core/utf16-slice so truncation counts full code points instead of UTF-16 code units. This matches the already-merged PR #101517 (session-cost-usage).

User Impact

Gateway status log-tail summaries no longer show when error messages contain emoji near truncation boundaries. Existing column width caps are preserved — the broken surrogate is dropped cleanly rather than rendered as a replacement character.

Evidence

Real environment tested: local OpenClaw source checkout, Node v24.13.1, PR head.

Exact steps after this patch:

node --import tsx -e "
const { truncateUtf16Safe } = await import('@openclaw/normalization-core/utf16-slice');
const content = 'x'.repeat(78) + '\u{1F680}xx';
const before = content.slice(0, 79) + '…';
const after = truncateUtf16Safe(content, 79) + '…';
const t = (s) => /[\uD800-\uDBFF](?![\uDC00-\uDFFF])/.test(s);
console.log('BEFORE (.slice 79):', JSON.stringify(before.slice(-12)), 'lone:', t(before));
console.log('AFTER  (truncateUtf16Safe 79):', JSON.stringify(after.slice(-12)), 'lone:', t(after));
"

Evidence after fix:

BEFORE (.slice 79):           "yyyyyyyyyy\ud83d…"  lone: true
AFTER  (truncateUtf16Safe 79): "yyyyyyyyyyy…"       lone: false

Observed result after fix: truncateUtf16Safe preserves full code points; no lone surrogate reaches terminal output. The existing maxLen cap is unchanged — the broken surrogate is dropped cleanly rather than rendered as .

What was not tested: full openclaw gateway status end-to-end run on exact PR head (production CLI uses installed binary).

Regression Test Plan

node scripts/run-vitest.mjs src/commands/status-all/gateway.test.ts --run
  Test Files  1 passed (1)
       Tests  3 passed (3)

Includes a focused regression test ("summarizeLogTail truncation / does not split surrogate pairs") that verifies the shorten helper drops a broken surrogate pair cleanly.

AI-assisted.

@openclaw-barnacle openclaw-barnacle Bot added commands Command implementations size: XS labels Jul 7, 2026
@clawsweeper

clawsweeper Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Thanks for the context here. I swept through the related work, and this is now duplicate or superseded.

This PR is a valid narrow bug fix, but it is superseded by the broader open UTF-16 truncation sweep that carries the same gateway status change, a better caller-level regression test, maintainer proof, and contributor credit.

Root-cause cluster
Relationship: superseded
Canonical: #101654
Summary: The current PR is a narrow instance of the same UTF-16-safe truncation sweep now owned by the broader canonical PR.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Canonical path: Use #101654 as the canonical landing path for this bug because it includes this gateway fix, caller-level coverage, broader duplicate cleanup, and contributor credit.

So I’m closing this here and keeping the remaining discussion on #101654.

Review details

Best possible solution:

Use #101654 as the canonical landing path for this bug because it includes this gateway fix, caller-level coverage, broader duplicate cleanup, and contributor credit.

Do we have a high-confidence way to reproduce the issue?

Yes. Source inspection shows current main raw-slices the status-all gateway OAuth diagnostic message before appending an ellipsis, so a surrogate pair at that boundary can leave a dangling surrogate; I did not run the full CLI path in this read-only review.

Is this the best way to solve the issue?

No, this PR is no longer the best landing path. The code change is now correct, but the broader canonical PR carries the same gateway fix with better caller-level coverage and consolidates overlapping UTF-16 truncation work.

Security review:

Security review cleared: The current PR only changes TypeScript string truncation and tests, with no dependency, workflow, secret, permission, network, or code-execution surface added.

AGENTS.md: found and applied where relevant.

What I checked:

Likely related people:

  • steipete: Git blame and log show Peter Steinberger introduced src/commands/status-all/gateway.ts; the open superseding sweep is authored by the same login and includes this gateway fix. (role: introduced current behavior and canonical sweep owner; confidence: high; commits: 395fbb8eb631, 080eb95c554c; files: src/commands/status-all/gateway.ts, src/commands/status-all/gateway.test.ts)
  • wm0018: Recent merged status-all work moved sibling issue-message truncation to truncateUtf16Safe, matching the safe fix shape used here. (role: recent adjacent contributor; confidence: medium; commits: 5511b6fcafcc; files: src/commands/status-all/report-lines.ts)
  • maweibin: This contributor authored this PR and also has merged same-class UTF-16 truncation fixes in nearby CLI/runtime surfaces, so they have relevant context beyond this proposal. (role: adjacent truncation contributor; confidence: medium; commits: afdb9fd26408, 4d5cd05a64bf; files: src/infra/session-cost-usage.ts, src/commands/tasks.ts)
  • Alix-007: History shows the shared UTF-16 slicing helper used by both this PR and the superseding sweep was introduced by this contributor. (role: shared helper introducer; confidence: medium; commits: 240487179b69; files: packages/normalization-core/src/utf16-slice.ts)

Codex review notes: model internal, reasoning high; reviewed against 60f0749b7fb3.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. labels Jul 7, 2026
…s truncation

Use Array.from() to count full code points instead of UTF-16 code
units, preventing lone surrogates in gateway status output.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
@maweibin
maweibin force-pushed the fix/status-gateway-emoji-truncation branch from 41d2533 to 244cca9 Compare July 7, 2026 12:19
@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. labels Jul 7, 2026
@steipete

steipete commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Thanks @maweibin. The useful behavior from this PR was consolidated with the sibling UTF-16 boundary fixes into #101654 and landed on main in a9582a1bb62aa70bb0b0ceb72536d0d76f08eab8.

The landed fix uses the existing truncateUtf16Safe helper, preserves this caller's prior code-unit limit and output shape, and adds caller-level surrogate-boundary coverage. Closing this PR as superseded by the canonical landed change.

@steipete steipete closed this Jul 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

commands Command implementations P2 Normal backlog priority with limited blast radius. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. size: XS status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants