bytecodealliance / bytecodealliance/wasm-pkg-tools
`generate_resolve` merges full Component resolve, causing dep resolution panics
- Dominant language
- Rust
- Stars
- 160
- Forks
- 42
- Avg merge
- 7h 57m
- Merged PRs (30d)
- 3
Description
Context: I'm building [asterai](https://asterai.io), a WASM component registry & runtime (to bundle & run components on top of wasmtime). Hit this issue when resolving a dependency on a published component's interface.
The issue is with `wasm-pkg-core`'s dependency resolution when a dependency is a compiled Component binary (rather than a WIT-only package). The component's runtime world (with all its WASI imports) gets merged into the dep resolution, which causes `wit-parser` to panic.
## Versions
- `wasm-pkg-core` 0.15.0
- `wit-parser` 0.244.0
- `wit-component` 0.244.0
## Reproduction
### 1. Publish a compiled component to an OCI registry
The component's WIT source is simple, no WASI deps:
```wit
package asterai:discord@0.1.0;
interface types {
record message {
content: string,
channel-id: string,
}
}
interface discord {
send-message: func(content: string, channel-id: string) -> string;
}
world component {
export discord;
export types;
}
```
The compiled `component.wasm` (built from Rust with `cargo component build`) includes WASI runtime imports: `wasi:io`, `wasi:cli`, `wasi:filesystem`, `wasi:http`, etc.
Push the compiled component binary to an OCI registry at the version tag `asterai/discord:0.1.0`.
### 2. Build a WIT package that depends on it
```wit
package asterai:discord-message-listener@0.1.0;
interface incoming-message {
use asterai:discord/types@0.1.0.{message};
handle: func(msg: message);
}
world component {
export wasi:cli/run@0.2.0;
}
```
Call `resolve_dependencies` followed by `generate_resolve` (or `build_package` / `fetch_dependencies` which call it internally).
### 3. Result
```
thread 'tokio-runtime-worker' panicked at wit-parser-0.244.0/src/resolve.rs:1807:25:
world import of wasi:filesystem/types@0.2.0 is missing transitive dep of wasi:io/streams@0.2.0
```
Without the `asterai:discord` dependency (i.e. only WIT-only deps from wasi.dev), everything works fine.
## Root cause
In `resolver.rs` (L740), `generate_resolve` handles `DecodedWasm::Component` identically to `DecodedWasm::WitPackage`:
```rust
DecodedDependency::Wasm { resolution, decoded } => {
let resolve = match decoded {
DecodedWasm::WitPackage(resolve, _) => resolve,
// merges full runtime resolve!
DecodedWasm::Component(resolve, _) => resolve,
};
merged.merge(resolve).with_context(|| ...)?;
}
```
For a `Component`, the resolve includes:
- The component's exported package interfaces (what consumers actually need)
- A runtime world with imports for all WASI interfaces (`wasi:io`, `wasi:cli`, `wasi:filesystem`, etc.)
- Full definitions of all those WASI packages
When this is merged, and then the separately-fetched wasi.dev WIT packages are also merged, `assert_valid()` iterates all worlds in the resolve, including the component's runtime world, and `assert_world_elaborated` finds broken cross-references from the duplicate WASI package merging.
I believe the runtime world is an implementation detail that shouldn't leak into dep resolution. Consumers only need the package's exported interfaces.
## Suggested fix
When `decoded` is `DecodedWasm::Component`, sanitize before merging:
1. Find the interface package by matching `resolve.packages` against the dependency name (note: `resolve.worlds[world_id].package` points to a synthetic `root:component` package, not the actual interface package)
2. Strip worlds from that package
3. Re-encode as WIT-only
4. Decode the result (yields a `DecodedWasm::WitPackage` with a clean resolve)
5. Merge that clean resolve instead
This is what we're doing as a workaround on our side (reimplementing `generate_resolve` with the sanitization step) and it resolves the issue.
I'm happy to submit a PR if this approach looks right.
By the way: `assert_world_elaborated` in `wit-parser` (`resolve.rs:1807`) uses `assert!` instead of returning an `Err`. So the validation failure panics the process. In a server context, this crashes the tokio worker thread. This might warrant a separate issue on `bytecodealliance/wasm-tools`? Though maybe `assert!` is reasonable in this scenario.
Contributor guide
Assessment
This issue has not been assessed yet.