Skip to content

[Bug]: Session tables corrupt Unicode keys at truncation boundaries #103001

Description

@steipete

Bug type

Behavior bug (incorrect terminal output without a crash)

Beta release blocker

No

Summary

Long session keys can render replacement characters in openclaw sessions and openclaw sessions cleanup --dry-run when a supplementary-plane Unicode character crosses either retained truncation boundary.

Steps to reproduce

  1. Save a session store containing a key whose first retained segment ends on the high surrogate of an emoji and whose retained suffix starts on the matching low-surrogate boundary:
node <<'NODE'
const fs = require("node:fs");
const key = `${"a".repeat(15)}😀middle😀${"z".repeat(5)}`;
fs.writeFileSync(
  "/tmp/openclaw-sessions-utf16.json",
  JSON.stringify({
    [key]: {
      sessionId: "utf16-repro",
      updatedAt: Date.now(),
      model: "openai/gpt-5.5"
    }
  })
);
NODE
  1. Run openclaw sessions --store /tmp/openclaw-sessions-utf16.json --limit all.
  2. Inspect the rendered key cell.

The formatter's current operation can also be isolated directly:

const key = `${"a".repeat(15)}😀middle😀${"z".repeat(5)}`;
const rendered = `${key.slice(0, 16)}...${key.slice(-6)}`.padEnd(26);
console.log(JSON.stringify(rendered));
// "aaaaaaaaaaaaaaa\ud83d...\ude00zzzzz "

Encoding that string for terminal output produces aaaaaaaaaaaaaaa�...�zzzzz .

Expected behavior

The fixed-width session key cell should retain the existing prefix/suffix layout without emitting unpaired UTF-16 surrogate halves or replacement characters.

Actual behavior

src/commands/sessions-table.ts truncates with raw slice(0, 16) and slice(-6). For the key above, those slices retain one dangling high surrogate and one dangling low surrogate, which become replacement characters in UTF-8 terminal output.

OpenClaw version

Confirmed in the source for shipped tags v2026.4.27, v2026.6.11, and v2026.7.1-beta.2, and current main at 4f9a371c9530e9d2eb2912a44054a44228a59612.

Operating system

Platform-independent Node.js string/UTF-8 terminal behavior; source reproduction run on macOS.

Install method

Source checkout; shipped-tag source verified.

Model

Not applicable. The bug occurs while formatting persisted session keys before model execution.

Provider / routing chain

Not applicable.

Logs

keyLength=30
renderedLength=26
danglingSurrogates=[{"index":15,"hex":"d83d"},{"index":19,"hex":"de00"}]
utf8=aaaaaaaaaaaaaaa�...�zzzzz 

Impact and severity

  • Affected users: operators listing or previewing cleanup for long session keys containing emoji or other supplementary-plane characters at either truncation boundary.
  • Severity: low; display corruption only, with no persisted-session data loss.
  • Frequency: deterministic for keys crossing either fixed boundary.
  • Consequence: key cells show misleading replacement characters, making otherwise distinct session identifiers harder to recognize.

Additional information

Both terminal entry points share formatSessionKeyCell, so one fix at that formatter covers the normal sessions table and cleanup dry-run table. JSON session output is not truncated and is unaffected. The canonical truncateUtf16Safe and sliceUtf16Safe helpers already preserve the existing UTF-16-unit budgets without splitting surrogate pairs.

Metadata

Metadata

Assignees

Labels

P2Normal backlog priority with limited blast radius.bugSomething isn't workingclawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:ux-frictionUser-facing flow adds avoidable confusion or support burden without fully blocking progress.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.maintainerMaintainer-authored PRmaturity:stableIssue affects a taxonomy feature currently scored M4/M5.

Type

No type

Fields

Priority

None yet

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions