Skip to content

REST: stream names containing / must be percent-encoded, and the unencoded 404 gives no hint #635

Description

@shikhar

Stream names with / are idiomatic in S2 (cdc/products, events/{tenant}, prefix-based listing), but using one in a REST path without percent-encoding fails with an empty 404 that's hard to diagnose.

Repro (s2-lite 0.39.1, and identical on s2.dev)

s2 create-stream s2://my-basin/cdc/products

# unencoded slash → 404, empty body
curl -s -w '%{http_code}' "$ENDPOINT/v1/streams/cdc/products/records?seq_num=0&count=1" \
  -H "Authorization: Bearer $TOKEN" -H "s2-basin: my-basin"
# → 404 (no body)

# percent-encoded → 200
curl -s -w '%{http_code}' "$ENDPOINT/v1/streams/cdc%2Fproducts/records?seq_num=0&count=1" \
  -H "Authorization: Bearer $TOKEN" -H "s2-basin: my-basin"
# → 200

Both s2-lite and the cloud service behave the same way, so this is not a parity bug — filing here since the OpenAPI spec and REST surface are defined in this repo.

Why full unencoded support is likely off the table

Routes like /streams/{stream} and /streams/{stream}/records are ambiguous under unencoded slashes: /streams/cdc/products/records could be stream cdc/products + /records, or stream cdc/products/records (itself a valid name) via /streams/{stream}. So requiring percent-encoding seems correct — the gap is discoverability:

  1. The failure mode is a bodyless 404. A miss on an encoded-but-nonexistent stream returns a helpful {"code":"stream_not_found",...} body; the unencoded routing miss returns nothing. A fallback route under /v1/streams/ that returns a JSON error hinting at percent-encoding (or a did you mean cdc%2Fproducts-style message) would turn a head-scratcher into a one-look fix.
  2. The requirement is undocumented. Neither the protocol docs nor the OpenAPI spec's stream path parameter description mention that / in stream names must be encoded as %2F. A sentence on the parameter description (it's generated from this repo) plus one in the protocol docs would cover it.

🤖 Filed with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions