session: count framed payload lengths in characters, not bytes - #7
Merged
Conversation
Each command response is framed as <id>:<length>:<payload>, and that
length counts characters — the service produces it the way JavaScript's
String.length and Java's String.length() do. dwshell sliced the body by
bytes, which agrees only while everything is ASCII: one non-ASCII
character and the payload is cut short, leaving JSON that ends
mid-structure.
Found against a live account, where a rejected agent name came back as
K:K11:71:K:{"message":"L'agente 'x' già esiste.","status":"error"}
71 characters, 72 bytes. dwshell took 71 bytes, lost the closing brace,
and reported "unexpected end of JSON input" instead of the service's
own message.
The blast radius is narrower than it first appears, and worth recording:
the agent escapes non-ASCII in its own responses (\u00e0), so anything
coming from an agent — file listings with accented names included — was
never affected. It is the account channel, where the service answers
with literal UTF-8, that truncates. Localized error messages are the
common case, which is why this surfaced only on an Italian account.
Counted in UTF-16 code units rather than Go runes: for anything up to
U+FFFF the two agree, and beyond it the service counts two, so runes
would have traded one off-by-one for another.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01MvidAFW9a2r4hTgHPW9ywG
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A pre-existing bug, found while validating agent management against a live account.
The bug
Every command response is framed as
<id>:<length>:<payload>, and that length counts characters — the service produces it the way JavaScript'sString.lengthand Java'sString.length()do. dwshell sliced the body by bytes, which agrees only while everything is ASCII. One non-ASCII character and the payload is cut short, leaving JSON that ends mid-structure.Captured live, a rejected agent name:
71 characters, 72 bytes. dwshell took 71 bytes, lost the closing brace, and reported:
instead of the service's own message.
Blast radius — narrower than it looks, and worth recording
The agent escapes non-ASCII in its own responses (
à), so anything coming from an agent was never affected — file listings with accented names included. I verified this:dwshell lsover a directory containingcittà-più-perché.txtworks correctly even without this fix, because the agent sends the name escaped and the payload stays pure ASCII.It is the account channel that truncates, where the service answers with literal UTF-8. Localized error messages are the common case, which is why this only surfaced on an Italian account.
The fix
Payload lengths are consumed in UTF-16 code units rather than Go runes: up to U+FFFF the two agree, and beyond it the service counts two, so counting runes would have traded one off-by-one for another. There is a test for each.
Verification
unexpected end of JSON inputL'agente 'x' già esiste.lsover accented filenames (live)gofmt,go vet,go test -race ./...green.🤖 Generated with Claude Code
https://claude.ai/code/session_01MvidAFW9a2r4hTgHPW9ywG