Skip to content

fix(request): teach JSON escape + MCP double-escape on control-char parse error (#491)#1098

Merged
ohdearquant merged 9 commits into
mainfrom
fix/request-parse-error-491
Jul 18, 2026
Merged

fix(request): teach JSON escape + MCP double-escape on control-char parse error (#491)#1098
ohdearquant merged 9 commits into
mainfrom
fix/request-parse-error-491

Conversation

@ohdearquant

Copy link
Copy Markdown
Owner

Summary

A raw control-character byte inside a quoted string in the function-call DSL surfaced the bare
serde_json message ("control character ... found while parsing a string"), which teaches nothing
and sends callers toward the wrong workaround. This wraps the serde_json string-decode error at the
parse_value call site so the message names the offending character and position, states that DSL
string escapes follow JSON (\n, \t, \", \\), and notes that callers passing ops through a
JSON transport (every MCP client) must double-escape (\\n in the wire form, because the transport
decodes one level before the parser runs). Grammar unchanged (ADR-016 / ADR-084 §3c basis).

Scope note

PR #957 already normalizes raw \n/\r/\t bytes before they reach serde_json, so those no
longer error at all. The bare-serde-message problem remained for the other raw control bytes (NUL,
form feed, ESC, ...); this addresses the error message for that remaining path.

Tests

control_char_error_teaches_escape_syntax_and_mcp_double_escape feeds a raw form-feed byte in a
quoted value and asserts the error is DslError::InvalidValue with a message carrying the escape
grammar and the double-escape guidance. Non-control-character parse errors pass through unchanged.

Gates: cargo test -p khive-request (122 passed), cargo fmt --all -- --check,
cargo clippy -p khive-request --all-targets -- -D warnings all clean.

Closes #491.

Leo and others added 4 commits July 17, 2026 21:18
…he helper, derive from source byte (#491)

Route quoted object-key JSON string decoding through the same
decode_quoted_json_string helper used by quoted values, so the ADR-084 §3c
escape-grammar/MCP-double-escape diagnostic applies to both paths instead of
only values. Classify and locate the offending control byte by scanning the
known quoted source span directly (find_offending_control_byte) rather than
matching serde_json::Error's message text, and bound that scan to the
already-known quoted span instead of rescanning the full ops source. Fix a
misattached rustdoc comment that had described quoted_prev_path_is_valid
while sitting above the now-removed describe_value_parse_error.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…re + borrow-fast-path the decoder (#491)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
… and cover all post-backslash control bytes (#491)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@oceanwaves630 oceanwaves630 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed against the verdict at head 3e555b9: zero blocking items, minors filed as follow-up. Approving.

Leo added 4 commits July 18, 2026 07:28
…y from a broken control-byte pair (#491)

A `\u` escape short by exactly 2 hex digits has its 3rd hex-digit slot
land on a raw backslash and its 4th slot land on a raw control byte. The
control byte is then physically adjacent to a backslash, so the scanner
recorded it as a genuine broken `\<ctrl>` pair and the parse-error guard
appended the MCP double-escape guidance for what is actually a malformed
unicode escape (both fail with serde_json's identical InvalidEscape kind
and Display text, so the guard cannot tell them apart by the error alone).

normalize_quoted_string now treats the 4 characters following `\u` as an
opaque hex-digit-slot window, mirroring serde_json's decode_hex_escape:
they are copied through verbatim and never re-enter the general
backslash-pair branch, so a byte inside that window is never recorded as
a ControlByteHit regardless of what it is.
Object keys decoded through the same normalize_quoted_string path as
argument values, so a raw literal newline/CR/tab byte inside a quoted
object key parsed successfully instead of being rejected. Per ADR-016
and PR #957, that literal-control-byte carve-out is scoped to quoted
argument values only. Keys now decode via strict serde_json semantics,
independent of the value-path normalization and its diagnostic
enrichment.
…e value (#491)

The value-position diagnostic counted the byte offset from the span opening
quote, so it reported an index one past the actual position within the value.
Report it relative to the value; add a regression test asserting a leading
control byte is byte 0 of the value.
…inuation (#491)

A high-surrogate `\u` escape (U+D800-U+DBFF) must be followed by a `\u`
low-surrogate escape. When the byte after the continuation backslash is a raw
control byte, serde_json fails on the malformed surrogate pair, but the
quoted-string scanner reset its escape state after the four hex slots and
recorded that `\<ctrl>` as a genuine broken control-byte pair. The enriched
error then attached the JSON double-escape guidance to a failure the control
byte does not explain.

Track high-surrogate completion in normalize_quoted_string so the immediate
continuation control byte is not recorded as a control-byte hit; the error
stays serde_json's own surrogate message. A broken `\<ctrl>` pair after a
complete surrogate pair is unaffected and still receives the guidance.

Also genericize the double-escape hint: it described the JSON transport as
"every MCP client", which conflicts with the transport-agnostic contract of
ADR-016. The hint now refers to a JSON transport generally.
@ohdearquant
ohdearquant marked this pull request as ready for review July 18, 2026 15:02
@ohdearquant
ohdearquant merged commit c323064 into main Jul 18, 2026
25 checks passed
@ohdearquant
ohdearquant deleted the fix/request-parse-error-491 branch July 18, 2026 15:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

khive-request: raw control-char parse error should teach the escape syntax and MCP double-escape

2 participants