Symptom
Any consumer on structured-proxy 1.0.2 building the transcoding router over a DescriptorPool whose google.api.http rules use {param} path templates panics at startup:
thread 'tokio-rt-worker' panicked at src/transcode/mod.rs:99:
Path segments must not start with `:`. For capture groups, use `{capture}`.
The panic comes from axum 0.8 / matchit inside router.route(&axum_path, ...). gRPC is unaffected; the entire REST transcoding listener is dead.
Root cause
1.0.2 bumped axum 0.7 → 0.8 but proto_path_to_axum (src/transcode/mod.rs) was not migrated. It still converted {id} → :id (axum 0.7 syntax). axum 0.8 uses {param} and rejects any segment starting with :.
Evidence the migration was partial:
- The alias branch in the same file already uses the axum-0.8
{path} form.
- The unit test
test_proto_path_to_axum still asserted the 0.7 output.
Fix
axum 0.8's capture syntax matches the google.api.http {param} form, so plain params pass through verbatim. Wildcard/field-path templates are rewritten:
{name=*} → {name}
{name=**} → {*name} (catch-all)
- bare
* / ** → position-named captures
Updated the unit test to assert 0.8 output and added a regression test that builds the real router over a brace-param path (the panic site) and asserts no panic.
Release
semantic-release cuts 1.0.3 from the fix: commit. Do NOT yank 1.0.2 (carries the jsonwebtoken 10.4.0 / GHSA-h395 fix); 1.0.3 keeps it and repairs transcoding.
Acceptance criteria
Estimate: 1h
Symptom
Any consumer on structured-proxy 1.0.2 building the transcoding router over a
DescriptorPoolwhosegoogle.api.httprules use{param}path templates panics at startup:The panic comes from axum 0.8 / matchit inside
router.route(&axum_path, ...). gRPC is unaffected; the entire REST transcoding listener is dead.Root cause
1.0.2 bumped axum 0.7 → 0.8 but
proto_path_to_axum(src/transcode/mod.rs) was not migrated. It still converted{id}→:id(axum 0.7 syntax). axum 0.8 uses{param}and rejects any segment starting with:.Evidence the migration was partial:
{path}form.test_proto_path_to_axumstill asserted the 0.7 output.Fix
axum 0.8's capture syntax matches the
google.api.http{param}form, so plain params pass through verbatim. Wildcard/field-path templates are rewritten:{name=*}→{name}{name=**}→{*name}(catch-all)*/**→ position-named capturesUpdated the unit test to assert 0.8 output and added a regression test that builds the real router over a brace-param path (the panic site) and asserts no panic.
Release
semantic-release cuts 1.0.3 from the
fix:commit. Do NOT yank 1.0.2 (carries the jsonwebtoken 10.4.0 / GHSA-h395 fix); 1.0.3 keeps it and repairs transcoding.Acceptance criteria
proto_path_to_axumemits axum 0.8{param}syntaxEstimate: 1h