Skip to content

Unexpected behavior: sliceUtf16Safe swaps bounds when end < start instead of returning "" like String.prototype.slice #99980

Description

@devin-ai-integration

What happened

sliceUtf16Safe documents itself as a surrogate-safe slice, but when end < start it silently swaps the bounds and returns the middle of the string, whereas String.prototype.slice (which it otherwise mirrors, including negative-index handling) returns "" in that case.

Code:

/** Slices a UTF-16 string without returning dangling surrogate halves at either edge. */
export function sliceUtf16Safe(input: string, start: number, end?: number): string {
const len = input.length;
let from = start < 0 ? Math.max(len + start, 0) : Math.min(start, len);
let to = end === undefined ? len : end < 0 ? Math.max(len + end, 0) : Math.min(end, len);
if (to < from) {
const tmp = from;
from = to;
to = tmp;
}
if (from > 0 && from < len) {
const codeUnit = input.charCodeAt(from);
if (isLowSurrogate(codeUnit) && isHighSurrogate(input.charCodeAt(from - 1))) {
from += 1;
}
}
if (to > 0 && to < len) {
const codeUnit = input.charCodeAt(to - 1);
if (isHighSurrogate(codeUnit) && isLowSurrogate(input.charCodeAt(to))) {
to -= 1;
}
}
return input.slice(from, to);
}

Repro

sliceUtf16Safe("hello", 4, 1); // "ell"
"hello".slice(4, 1);           // ""

Expected

Match String.prototype.slice semantics (return "" when to <= from) so callers can substitute it for .slice safely — that drop-in substitution is its purpose (see truncateUtf16Safe in the same file, and re-exports from src/utils.ts).

Impact

Any caller that computes a start/end pair where the end can land before the start (e.g. index math around markers) gets an unexpected non-empty substring instead of "", which is a subtle logic-corruption footgun for a widely re-exported helper.

Environment

Current main (4287d26).

Found via source review (AI-assisted).

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Normal backlog priority with limited blast radius.clawsweeper:current-main-reproClawSweeper found a high-confidence current-main issue reproduction.clawsweeper:fix-shape-clearClawSweeper found a clear likely implementation shape for this issue.clawsweeper:queueable-fixClawSweeper marked this issue as an existing queue_fix_pr work candidate.impact:otherThis issue has meaningful maintainer-visible impact outside the owned taxonomy.issue-rating: 🦀 challenger crabExceptional issue quality: high-confidence current-main reproduction and actionable evidence.no-staleExclude from stale automation

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions