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
Messages API returns bare 400 with no error body for lone UTF-16 surrogates in message content
#9010
Symptom: A session whose message history contains a lone high surrogate (e.g. an emoji cut in half by a UTF-16-code-unit-level truncation, leaving U+D83D unpaired) fails on every turn with:
The HTTP 400 response body carries no error.message (empty/non-JSON), so providerError() in dsh-llm-deepseek falls back to the bare request failed (400) text — the user gets zero diagnostic signal about what was invalid. In our case a single unpaired \ud83d inside an injected runtime-context line caused 8 consecutive turn failures, while other providers (GLM, Kimi) accepted the exact same history silently.
Repro (deterministic, same channel/model, only delta = one lone surrogate):
Send a message whose content contains the lone surrogate \ud83d (as a single unpaired UTF-16 code unit in the string) to deepseek-flash → request fails with 400, empty error body.
Send the identical message without the lone surrogate → 200, normal completion.
Analysis:
The Messages endpoint (or an upstream gateway) rejects lone surrogates in JSON string values — reasonable, but the response body is empty/non-JSON, so the SDK can only surface (400).
Interestingly, the input pipeline elsewhere sanitizes this (stdin-read task text gets lone surrogates replaced with U+FFFD), so the bad character can only enter via programmatically-injected context — which is exactly how it slipped through for us.
Asks (either or both):
Server side: include a standard {"error":{"message":...}} body for this rejection (e.g. "invalid UTF-16: lone surrogate in message content") so clients can self-diagnose.
Adapter side (dsh-llm-deepseek): consider pre-validating/sanitizing lone surrogates in outbound message text (replace with U+FFFD or strip), mirroring what the input pipeline already does — or at minimum surface the raw response body in the thrown error.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Environment: DSH 0.2.0-rc.2, provider
deepseek-official, modeldeepseek-flash(Anthropic-format Messages endpoint), Node 24.Symptom: A session whose message history contains a lone high surrogate (e.g. an emoji cut in half by a UTF-16-code-unit-level truncation, leaving
U+D83Dunpaired) fails on every turn with:The HTTP 400 response body carries no
error.message(empty/non-JSON), soproviderError()indsh-llm-deepseekfalls back to the barerequest failed (400)text — the user gets zero diagnostic signal about what was invalid. In our case a single unpaired\ud83dinside an injected runtime-context line caused 8 consecutive turn failures, while other providers (GLM, Kimi) accepted the exact same history silently.Repro (deterministic, same channel/model, only delta = one lone surrogate):
\ud83d(as a single unpaired UTF-16 code unit in the string) todeepseek-flash→ request fails with 400, empty error body.Analysis:
(400).Asks (either or both):
{"error":{"message":...}}body for this rejection (e.g. "invalid UTF-16: lone surrogate in message content") so clients can self-diagnose.dsh-llm-deepseek): consider pre-validating/sanitizing lone surrogates in outbound message text (replace with U+FFFD or strip), mirroring what the input pipeline already does — or at minimum surface the raw response body in the thrown error.All reactions