Skip to content

fix(translation): accept SSE data fields with no space after the colon - #447

Open
pucedoteth wants to merge 1 commit into
NVIDIA-NeMo:mainfrom
pucedoteth:fix/sse-data-field-optional-space
Open

fix(translation): accept SSE data fields with no space after the colon#447
pucedoteth wants to merge 1 commit into
NVIDIA-NeMo:mainfrom
pucedoteth:fix/sse-data-field-optional-space

Conversation

@pucedoteth

@pucedoteth pucedoteth commented Aug 15, 2026

Copy link
Copy Markdown

Closes #399.

Problem

parse_json_sse_frame matched the data field with a literal prefix that includes the space:

.filter_map(|line| line.strip_prefix("data: ").map(|l| l.to_string()))

The SSE spec treats that space as optional framing, not part of the delimiter:

Collect the characters on the line before the first U+003A COLON character (:), and let field be that string.
Collect the characters on the line after the first U+003A COLON character (:), and let value be that string. If value starts with a U+0020 SPACE character, remove it from value.

So data:{"delta":"hi"} is a well-formed frame that this parser dropped. The frame then folded to an empty string and decoded to SseFrame::Empty, silently discarding the event — including a data:[DONE] terminator, which stopped being recognised as the end of the stream.

Fix

Split on the first colon and match the field name, stripping at most one leading space from the value:

let value = match line.split_once(':') {
    Some(("data", value)) => value,
    None if line == "data" => "",
    _ => return None,
};
Some(value.strip_prefix(' ').unwrap_or(value).to_string())

The None arm covers the spec's other case — a field line with no colon is the field name with an empty value.

Matching the field name rather than a prefix also keeps a line like database: {...} from being read as a data field. The old strip_prefix("data: ") already got that right by accident; it is now covered by a test so the new parsing can't regress it.

Comment lines are unaffected: they are filtered before this point, and :comment splits to a field name of "", which does not match data either way.

Tests

Three tests added to the existing module in sse.rs:

  • parses_a_data_line_without_a_space_after_the_colon — the reported case
  • done_marker_is_recognised_without_a_spacedata:[DONE] still terminates the stream
  • field_names_are_matched_exactlydatabase: is not a data field

The first two fail on main:

test sse::tests::done_marker_is_recognised_without_a_space ... FAILED
test sse::tests::parses_a_data_line_without_a_space_after_the_colon ... FAILED
Error: "expected a payload"

I also wrote a fourth test asserting that only one leading space is framing, then deleted it: this parser hands the value to serde_json, which ignores leading whitespace, so the distinction is not observable here. The behaviour is still spec-correct, it just isn't something this function can demonstrate, and I would rather not leave a test that passes for the wrong reason.

Gates

gate result
cargo fmt --all --check clean
cargo clippy --workspace --all-targets -- -D warnings clean
cargo test --workspace --exclude switchyard-py 501 passed, 0 failed
uv run ruff check . All checks passed!
uv run mypy switchyard Success: no issues found in 21 source files

Two pre-existing failures in my environment, both verified identical on a clean checkout of main and unrelated to this change:

  • cargo test --workspace fails to link switchyard-py (pyo3 symbol(s) not found for architecture arm64 — missing Python symbols in my local toolchain). Hence the --exclude above; I did not touch that crate.
  • uv run pytest tests/ fails tests/e2e/test_closed_book_proxy_integration.py, an e2e test that needs live services.

Commit is signed off per DCO.

Summary by CodeRabbit

  • Bug Fixes
    • Improved handling of streamed event data with or without a space after the separator.
    • Correctly processes empty data fields and terminal markers.
    • Prevents similarly named fields from being interpreted as valid event data.

@pucedoteth
pucedoteth requested a review from a team as a code owner August 15, 2026 23:18
@coderabbitai

coderabbitai Bot commented Aug 15, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: a86ca5cc-a414-4911-80e7-5465a8e7bd18

📥 Commits

Reviewing files that changed from the base of the PR and between 9ad6744 and 89b0bed.

📒 Files selected for processing (1)
  • crates/switchyard-translation/src/sse.rs

Included review availability: Your plan includes up to 12 reviews per rolling hour; 11 remain after this review.


Walkthrough

The SSE parser now accepts data fields with or without a space after the colon, accepts colonless data fields as empty values, and matches the field name exactly. Tests cover payloads, terminal markers, and similarly prefixed fields.

Changes

SSE parsing

Layer / File(s) Summary
Parse SSE data fields
crates/switchyard-translation/src/sse.rs
The parser recognizes exact data fields, supports optional separators and one leading space, and preserves empty colonless values. Tests cover no-space JSON payloads, no-space [DONE] markers, and rejection of fields such as database:.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to 89b0b

This change broadens SSE parsing to accept valid data fields without a space while preserving exact field matching and terminator handling. No actionable merge-blocking risk remains after normal checks and review.

Poem

I’m a rabbit with ears held high,
Parsing each data stream nearby.
Spaces may come, or spaces may flee,
[DONE] now ends the stream properly.
No false database hops by me!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the primary change: accepting SSE data fields without a space after the colon.
Linked Issues check ✅ Passed The parser changes and regression tests satisfy issue #399 by supporting both data formats, bare data lines, and exact field-name matching.
Out of Scope Changes check ✅ Passed The changes are limited to SSE parsing behavior and focused regression tests required by issue #399.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

Comment @coderabbitai help to get the list of available commands.

The frame parser matched `data: ` with `strip_prefix`, so a `data:` line
without the space was dropped. The frame then decoded to `SseFrame::Empty`
and the event was silently lost, including a `data:[DONE]` terminator.

Per the SSE spec the field name is everything before the first colon and
a single leading space is stripped from the value, so the space is
optional framing rather than part of the delimiter. Parse the field name
and value around the first colon instead, keeping any space beyond the
first as payload, and treat a bare `data` line as an empty value.

Matching on the field name also stops a line such as `database: {...}`
from being read as a `data` field, which the old prefix match already
handled correctly and is now covered by a test.

Closes NVIDIA-NeMo#399

Signed-off-by: pucedoteth <119044801+pucedoteth@users.noreply.github.com>
@pucedoteth
pucedoteth force-pushed the fix/sse-data-field-optional-space branch from 89b0bed to f7ad9ab Compare August 17, 2026 00:11
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.

[bug] SSE decoder drops data: fields without a space after the colon

1 participant