How to Fix: component-macro: Dependency conflict with wit-bindgen causes compilation failure
When wit-bindgen and wasmtime-component-macro come from different Git revisions, Cargo can compile two incompatible views of the same component-model ecosystem and fail hard.
This issue usually shows up when a project pulls both crates directly from Git, especially when one dependency tracks a newer commit than the other. Even though the crate names look compatible, the underlying WIT, component model, or shared internal types may have changed between commits, and Cargo treats those Git revisions as distinct package sources.
Understanding the Root Cause
The failure happens because wasmtime-component-macro and wit-bindgen are tightly coupled to evolving APIs in the Bytecode Alliance component toolchain. If your Cargo.toml pulls them from Git independently, Cargo may resolve:
- one crate from commit A
- another crate from commit B
That is enough to break compilation when:
- shared types moved or changed shape
- generated bindings expect a newer or older wit-bindgen API
- internal component-model crates are duplicated in the dependency graph
- macro expansion emits code against a different revision than the one your project compiles against
In practice, this is a classic Git dependency conflict: Cargo does not unify unrelated Git revisions automatically, even when they come from the same repository or ecosystem. If two crates depend on different commits of shared internals, Rust sees them as different packages, which can produce type mismatches, unresolved imports, trait implementation errors, or procedural macro failures.
This is especially common with fast-moving repositories such as the Bytecode Alliance stack, where wasmtime, wit-bindgen, and related component crates often need to be updated in lockstep. If you saw this after adopting direct Git dependencies rather than crates.io releases, that is the strongest signal that your dependency graph is split across incompatible revisions.
Step-by-Step Solution
The fix is to make sure your project resolves the relevant crates from a single compatible revision set. The safest approach is to pin both dependencies to commits that are known to work together.
1. Inspect your current dependency graph
Start by checking whether Cargo resolved multiple Git sources or revisions:
cargo tree -i wit-bindgen
cargo tree -i wasmtime-component-macro
cargo tree | grep -E "wit-bindgen|wasmtime|component"
If you see different Git URLs, branches, or commits for closely related crates, that is the conflict.
2. Pin both dependencies to matching commits
Instead of tracking moving branches, lock them to explicit revisions.
[dependencies]
wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "<compatible-commit>" }
wasmtime-component-macro = { git = "https://github.com/bytecodealliance/wasmtime", rev = "<compatible-commit>" }
Replace <compatible-commit> with revisions known to work together. A good strategy is:
- Find the wasmtime commit you need.
- Check that repository history, release notes, or linked issue discussion for the corresponding wit-bindgen revision.
- Pin both explicitly instead of using
mainormaster.
3. Prefer released versions when possible
If you do not need unreleased features, using crates.io versions avoids many Git synchronization problems:
[dependencies]
wit-bindgen = "<version>"
wasmtime = "<version>"
If your code specifically uses the macro crate, prefer the version that matches the rest of the wasmtime release family.
4. Use a Cargo patch to force a single source
If a transitive dependency pulls in an incompatible Git revision, override it with [patch] so everything resolves consistently.
[patch."https://github.com/bytecodealliance/wit-bindgen"]
wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "<compatible-commit>" }
You can do the same for related crates if needed. This is useful when one top-level dependency does not let you directly control its transitive Git source.
5. Regenerate bindings after aligning revisions
If your project checks in generated code or runs code generation during build, regenerate it after the dependency fix:
cargo clean
cargo update
cargo build
If you have a custom generation step, run that again too. Generated bindings created by an older wit-bindgen revision may not compile against a newer macro crate.
6. Verify with Cargo metadata
After pinning, confirm that only one logical revision path is being used:
cargo metadata --format-version 1
Look for duplicate package entries tied to different Git source identifiers. If duplicates remain, another crate is still introducing drift.
Example of a stable fix
A practical dependency setup often looks like this:
[dependencies]
wasmtime = { git = "https://github.com/bytecodealliance/wasmtime", rev = "abc123" }
wasmtime-component-macro = { git = "https://github.com/bytecodealliance/wasmtime", rev = "abc123" }
wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "def456" }
The exact commits matter. The key is that they must be from a known-compatible pairing, not just arbitrary latest commits from each repository.
Common Edge Cases
1. The crates compile, but generated code fails later
This usually means the dependency graph is aligned enough to build the macro, but your generated bindings still target an older ABI or API surface. Regenerate all bindings after updating revisions.
2. A transitive dependency reintroduces the wrong wit-bindgen
Even if your direct dependency is pinned, another crate may pull a different Git revision. Use cargo tree and [patch] to force one source.
3. Branch-based Git dependencies suddenly break CI
If you depend on branch = "main", a new upstream commit can break your build without any local code change. Pin rev in CI-sensitive projects.
4. Cargo.lock is stale across team members
One developer may have a working lockfile while another resolves newer branch heads. Commit Cargo.lock for binaries and ensure everyone rebuilds after dependency changes.
5. Macros and runtime crates are on different release lines
If you use a Git macro crate with a crates.io runtime crate, or vice versa, expect mismatches. Keep the macro, runtime, and generator crates on the same compatibility line.
6. Feature flags hide the real conflict
Sometimes the failure only appears when a component-related feature is enabled. Compare cargo tree -e features output between working and failing builds to spot hidden activations.
FAQ
Why does Cargo allow both dependencies if they are incompatible?
Cargo resolves versions and sources, not semantic compatibility across fast-moving Git commits. Two crates can be individually valid dependencies while still being incompatible at the API or generated-code level.
Can I fix this with cargo update alone?
Usually no. cargo update may refresh the lockfile, but if your manifests still point to drifting branches or conflicting Git sources, the problem will come back. Pinning compatible revisions is the durable fix.
Should I avoid Git dependencies entirely?
Not necessarily. Git dependencies are fine when you need unreleased fixes or features, but for tightly coupled ecosystems like wasmtime and wit-bindgen, you should pin exact commits and update them together.
The shortest path to a stable build is simple: stop tracking floating Git heads, pin compatible revisions, and ensure every component-model crate in your graph agrees on the same generation/runtime expectations.