bytecodealliance / bytecodealliance/wasm-pkg-tools

`generate_resolve` merges full Component resolve, causing dep resolution panics

Open
#191 0 comments 0 reactions 0 assignees View on GitHub
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.