You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Slack text cuts land on UTF-16 indexes at four sites: a surrogate pair astride a limit yields an invalid payload — the invalid_attachments outcome through a different door #3622
WebhookAlertService now cuts Slack text at four fixed character positions — the #3493 prose fragment cut and hard split, and #3618's SlackFieldText (2,000) and SlackHeading — all by UTF-16 index. A cut that lands between the two halves of a surrogate pair (any emoji or supplementary-plane character in a query text, database name, or advice string) produces an unpaired surrogate, which JsonSerializer escapes as a lone \uD83D-style unit and Slack rejects the whole payload as invalid — the same invalid_attachments outcome #3493 and #3618 were built to end, arriving through a different door, exactly once, on whichever page happens to carry an emoji at the boundary.
Fix
One helper — trim to the nearest char boundary (char.IsHighSurrogate(text[cut - 1]) → back off one) — applied at all four sites, with a pin that places a surrogate pair astride each limit and asserts the payload round-trips as valid UTF-16 and the omission count is stated in characters the reader would count. Also worth deciding: cut on the grapheme (StringInfo) rather than the code point, so a combining sequence isn't split either; cheap, and it makes the omission counts honest for the text the reader sees.
Found by #3618's review (non-blocking note; two of the four sites predate that PR, so it was left for a follow-up rather than folded). Related: #3493, #3612.
Record correction, post-merge: this issue's severity claim was wrong. System.Text.Json does not emit a lone surrogate escape — with the builder's own options it substitutes \uFFFD, the payload is valid, and Slack delivers it (measured on a net10.0 probe by the #3625 lane). So the defect was never a lost page; it was a page that lied — a � the value never held, a hard split showing two of them and no emoji, an omission count in UTF-16 units. #3625 fixed the lie (grapheme-boundary cuts at all six sites, omitted counts in characters). The invalid_attachments framing in the title should be read as refuted.
Activity
Slack text cuts land on whole characters, not UTF-16 indexes: an emoj…
erikdarlingdata commentedon Sep 18, 2026
Closed by the merge of #3625 (its body names this issue with a closing keyword; keywords do not fire against dev, so the watcher closes it).
erikdarlingdata commentedon Sep 18, 2026
Record correction, post-merge: this issue's severity claim was wrong.
System.Text.Jsondoes not emit a lone surrogate escape — with the builder's own options it substitutes\uFFFD, the payload is valid, and Slack delivers it (measured on a net10.0 probe by the #3625 lane). So the defect was never a lost page; it was a page that lied — a�the value never held, a hard split showing two of them and no emoji, an omission count in UTF-16 units. #3625 fixed the lie (grapheme-boundary cuts at all six sites, omitted counts in characters). Theinvalid_attachmentsframing in the title should be read as refuted.