Skip to content

fix(shared): keep assistant identity value truncation UTF-16 safe#102802

Closed
wangyan2026 wants to merge 1 commit into
openclaw:mainfrom
wangyan2026:fix/assistant-identity-utf16-safe
Closed

fix(shared): keep assistant identity value truncation UTF-16 safe#102802
wangyan2026 wants to merge 1 commit into
openclaw:mainfrom
wangyan2026:fix/assistant-identity-utf16-safe

Conversation

@wangyan2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

coerceIdentityValue() in assistant-identity-values.ts uses raw String.prototype.slice(0, maxLength) to truncate assistant identity labels. When the truncation boundary falls inside a UTF-16 surrogate pair (emoji, CJK extended), the resulting string contains a dangling surrogate that renders as U+FFFD (�) in user-visible assistant names and metadata.

Why This Change Was Made

Replace trimmed.slice(0, maxLength) with truncateUtf16Safe(trimmed, maxLength) from @openclaw/normalization-core/utf16-slice so the truncation point always falls on a complete code-point boundary. This preserves the existing code-unit cap while avoiding broken emoji in assistant identity values.

User Impact

  • Assistant names and identity labels no longer risk broken emoji when truncated
  • No behavior change for ASCII text
  • Negative maxLength now returns an empty string instead of slice(0, -1) (more correct for a truncation limit)

Evidence

  • node scripts/run-vitest.mjs src/shared/assistant-identity-values.test.ts — 5 tests passed
  • pnpm exec oxfmt --check — passed
  • pnpm exec oxlint — 0 warnings, 0 errors
  • git diff --check — passed
  • Regression: coerceIdentityValue("OpenClaw🚀🚀🚀🚀🚀", 11) returns "OpenClaw🚀" (not "OpenClaw🚀\ud83d" with a dangling surrogate)

coerceIdentityValue used raw slice(0, maxLength) to truncate assistant
identity labels. When the truncation boundary falls inside a UTF-16
surrogate pair (emoji, CJK extended), the resulting string contains a
dangling surrogate that renders as U+FFFD.

Replace slice(0, maxLength) with truncateUtf16Safe from
@openclaw/normalization-core/utf16-slice so the truncation point always
falls on a complete code-point boundary.
@vincentkoc vincentkoc self-assigned this Jul 9, 2026
@vincentkoc

Copy link
Copy Markdown
Member

Closing this as another isolated UTF truncation micro-change.

The production change is a two-line helper substitution, while the test also changes the separate negative-maxLength contract from slice(0, -1) behavior to an empty string. That semantic change is not needed for the surrogate-boundary fix and has no caller-contract proof here.

Assistant identity truncation should be handled as a bounded owner-level cleanup with the valid length contract made explicit, not as another one-call-site PR.

@steipete

steipete commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

The owner-level cleanup requested here has now landed via #103034 in bb16ca50d622b62baf56abd725c8d0e6b1e92619.

Instead of preserving coerceIdentityValue(value, maxLength), the landed change gives gateway and Control UI identity fields closed, typed field-to-limit maps, uses the fixed local-user name limit, and deletes the shared arbitrary-limit helper. That removes the accidental negative-limit contract while fixing split surrogate pairs at the real owners.

Validation at the exact reviewed head included green hosted CI and CodeQL, 42 sanitized AWS focused assertions, source-blind short-name/UTF-8 Gateway RPC proof, and clean fresh autoreview. Thanks @wangyan2026 for surfacing the boundary bug; the later contributor's original fix credit is preserved in the landed history.

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants