wit_component::decode (and wasm-tools component wit) panic with assertion failed: prev.is_none() at wit-parser/src/decoding.rs:1085 when given a valid component that imports a resource-bearing interface and re-exports the same imported instance under a different name.
Repro
(component
(type (;0;) (instance (export "request" (type (sub resource))))
)
(import "impl:test/handler" (instance (;0;) (type 0)))
(export "wasi:http/handler@0.3.0" (instance 0))
)
$ wasm-tools parse repro.wat -o repro.wasm
$ wasm-tools validate --features=all repro.wasm
$ echo $?
0
$ wasm-tools component wit repro.wasm
thread 'main' panicked at /…/wit-parser-0.247.0/src/decoding.rs:1085:9:
assertion failed: prev.is_none()
Expected
The component validates as well-formed (wasm-tools validate --features=all exits 0), so wit-parser should either decode it successfully or return a structured Err, not panic.
Actual
WitPackageDecoder::register_type_export allocates a fresh TypeId for the re-exported request resource and inserts it into self.resources[owner][name]. The map already has an entry from the import-side decoding, so the assert!(prev.is_none()) on line 1085 fires. Backtrace tail:
wit_parser::decoding::WitPackageDecoder::register_type_export at decoding.rs:1085
wit_parser::decoding::WitPackageDecoder::register_import at decoding.rs:821
wit_parser::decoding::WitPackageDecoder::decode_component_export at decoding.rs:724
wit_parser::decoding::ComponentInfo::decode_component at decoding.rs:340
wit_parser::decoding::decode_reader at decoding.rs:404
wit_parser::decoding::decode at decoding.rs:416
Why I hit this
I'm building a tool that generates a wrapper component around an existing component: the wrapper imports the original interface and re-exports it under the same canonical name (so downstream callers see no difference).
When the wrapped interface contains a resource (e.g. wasi:http/handler with request / response), wit_component::decode panics on the wrapper's bytes even though they validate cleanly. The minimal repro above is exactly that shape, stripped down to a single resource and a single wrapper-style re-export.
Versions
- wasm-tools CLI: 1.247.0
- wit-parser: 0.247.0
- wit-component: 0.247.0
(haven't bisected to find the first affected version)
Notes
- The same pattern with the resource type defined inline in the component (rather than imported from a sibling instance) decodes fine. It's specifically the alias chain through the import that confuses the resource-name registration.
- A defensible fix would be to teach
register_type_export to follow the alias chain and reuse the existing resource registration when the referenced type traces back to one already in self.resources[owner], instead of asserting.
- Even if the fix is non-trivial, replacing the assert! with a structured bail! would let downstream tools recover gracefully.
wit_component::decode(and wasm-tools component wit) panic with assertion failed:prev.is_none() at wit-parser/src/decoding.rs:1085when given a valid component that imports a resource-bearing interface and re-exports the same imported instance under a different name.Repro
(component (type (;0;) (instance (export "request" (type (sub resource)))) ) (import "impl:test/handler" (instance (;0;) (type 0))) (export "wasi:http/handler@0.3.0" (instance 0)) )Expected
The component validates as well-formed (wasm-tools validate --features=all exits 0), so wit-parser should either decode it successfully or return a structured
Err, not panic.Actual
WitPackageDecoder::register_type_exportallocates a fresh TypeId for the re-exported request resource and inserts it intoself.resources[owner][name]. The map already has an entry from the import-side decoding, so theassert!(prev.is_none())on line 1085 fires. Backtrace tail:Why I hit this
I'm building a tool that generates a wrapper component around an existing component: the wrapper imports the original interface and re-exports it under the same canonical name (so downstream callers see no difference).
When the wrapped interface contains a resource (e.g. wasi:http/handler with request / response),
wit_component::decodepanics on the wrapper's bytes even though they validate cleanly. The minimal repro above is exactly that shape, stripped down to a single resource and a single wrapper-style re-export.Versions
(haven't bisected to find the first affected version)
Notes
register_type_exportto follow the alias chain and reuse the existing resource registration when the referenced type traces back to one already in self.resources[owner], instead of asserting.