An agent CLI may depend on one crate for transport and another for saved data. Both crates may depend on the same codec crate. The transport crate asks for streaming support. The saved-data crate asks for JSON support. When both paths use the same dependency package in a normal build, Cargo enables both features on that package. The CLI does not need to request either feature directly. This is Cargo feature unification: Cargo uses the union of features requested for a shared dependency.
The requests start in separate manifests
Consider a CLI with two local dependencies:
[dependencies]
transport = { path = "../transport" }
archive = { path = "../archive" }
The transport crate declares its dependency this way:
[dependencies]
codec = { path = "../codec", default-features = false, features = ["stream"] }
The archive crate uses the same codec package and asks for a different feature:
[dependencies]
codec = { path = "../codec", default-features = false, features = ["json"] }
These names describe an example, not specific published crates. Each features entry requests a feature defined by codec. The Cargo features reference shows that dependency declarations can enable features this way. The path dependencies make the shared package explicit in this example.
Cargo combines the requests on codec
For this build, codec receives stream from transport and json from archive. Cargo builds the shared dependency with both enabled. A feature belongs to the package that defines it: a json feature on another package would be a separate feature, even though its name matches. Cargo’s dependency resolution guide also describes the union when different packages enable different features on one dependency.
This matters when reading a single manifest. The transport declaration tells you what that crate requests. It does not show the full feature set that codec receives in the CLI build. To understand the build, follow every path to the shared package.
Defaults can add another request
The example sets default-features = false on both paths so that its two explicit requests are easy to see. In a real graph, check every declaration of the shared dependency. Dependencies enable their default features unless a declaration disables them. If another path leaves defaults enabled, the resulting feature set can include those defaults even when one path specifies default-features = false. The features reference calls out this behavior for dependencies that appear multiple times.
The default feature can itself enable other features. Read the shared crate’s [features] table to see those links. A short dependency declaration may therefore activate more conditional code than its visible features list suggests.
The shared set affects conditional code
A crate can use #[cfg(feature = "stream")] or #[cfg(feature = "json")] to include code when those features are enabled. A feature can also enable another feature or an optional dependency. Those effects depend on how the shared crate defines its features. The Cargo features reference describes both conditional compilation and optional dependencies.
The union is useful when features add independent capabilities. Cargo’s guidance says features should be additive because packages elsewhere in the graph can enable combinations. If two features cannot safely coexist, the crate author must address that combination. The reference suggests detecting an incompatible pair with a compile error when the conflict cannot be avoided.
Build context changes the picture
The example uses normal dependencies in one CLI build. Other dependency kinds need closer inspection. Cargo’s resolver documentation explains that resolver version two avoids some feature unification across build dependencies, development dependencies, and dependencies for targets that are not being built. It also explains that features from dependencies of workspace packages are unified when those packages are built together. Separate Cargo invocations can produce different feature sets.
The selected target and command therefore belong in any feature investigation. A tree viewed while examining tests can differ from the dependencies relevant to a normal build. The cargo tree documentation says its display is a useful view of feature unification, while cautioning that it does not guarantee an exact picture of every compilation.
Trace the feature back to its source
From the CLI’s directory, run cargo tree -e features -i codec. The -e features option shows feature edges. The -i codec option reverses the tree so you can follow the paths that enable features on codec. The features reference recommends this command for finding why a package has a feature. The cargo tree guide explains how to read the reversed graph.
If the workspace contains other members that matter, add --workspace to include their reverse dependency paths. To scan enabled features more compactly, use cargo tree -f "{p} {f}". When the same package appears repeatedly, the tree may abbreviate repeated branches with (*); --no-dedupe expands them. These options are documented on the cargo tree command page.
What to do
- Find the shared package in each dependency path. Confirm that the paths resolve to the same package before combining their feature requests.
- Read every dependency declaration that reaches it. Record explicit features and whether each path enables defaults.
- Read the package’s
[features]table. Follow features that enable other features or optional dependencies. - Run
cargo tree -e features -i codecfor the package and build context you care about. Follow each enabled feature back to the crate that requested it. - Check the resulting combination against the shared crate’s code and documentation. If two features conflict, fix the crate or the dependency graph instead of assuming one request takes priority.

The Campfire
No commentsNobody has pulled up a log by this one yet. Be the first to say what you make of it.
Held for the desk. It appears after a look.