cargo build fails but cargo test passes: could not find net in actix_rt

After a dependency bump, a plain cargo build stopped with could not find net in actix_rt while cargo test kept passing. actix-server 2.8.0 never asks for the actix-rt features it needs, and updating it to 2.9.5, which does, fixes the build.

Errors or symptoms
could not find `net` in `actix_rt`unresolved import `actix_rt::net`
Affects
actix-server 2.8.0 · actix-rt 2.14 and newer · Cargo
Checked
with actix-server 2.9.5, actix-rt 2.15.0, rustc 1.98.1
Depth
deep
Tags
rust · cargo · dependencies · features

The error

Dependabot's grouped update moved actix-rt from 2.11.0 to 2.15.0 on 23 September. Only Cargo.lock changed. cargo test stayed green, but a plain cargo build stopped inside actix-server. This is what rustc 1.98.1 prints for it, error E0432:

error[E0432]: unresolved import `actix_rt::net`
  --> actix-server-2.8.0/src/builder.rs:3:15
   |
 3 | use actix_rt::net::TcpStream;
   |               ^^^ could not find `net` in `actix_rt`
   |
note: found an item that was configured out
  --> actix-rt-2.15.0/src/lib.rs:82:9
   |
81 | #[cfg(feature = "net")]
   |       --------------- the item is gated behind the `net` feature

cargo build --release, which my deploy runs, failed the same way. The note at the bottom is the real clue. The module is still there, but a feature switch keeps it out of the build.

Why cargo test still passed

My Cargo.toml lists actix-rt a second time, as a dev-dependency for async tests:

[dev-dependencies]
actix-rt = "2.11.0"

That entry keeps actix-rt's default features, and net and signal are among them. In the 2021 edition, Cargo's feature resolver version 2 only lets a dev-dependency switch features on when it builds something that uses dev-dependencies, like the test binaries. So cargo test and cargo build --all-targets compiled actix-rt with net and signal for every crate, actix-server included, because Cargo unifies the features every crate asks for. A plain cargo build left the dev-dependency out, and actix-rt got only what the rest of the tree asked for.

cargo tree shows both views. With -e normal it leaves dev-dependencies out, the way a plain build does. On the broken lockfile it printed this:

$ cargo tree -e normal -i actix-rt --depth 0 -f '{p} [{f}]'
actix-rt v2.15.0 []

$ cargo tree -i actix-rt --depth 0 -f '{p} [{f}]'
actix-rt v2.15.0 [actix-macros,default,macros,net,signal]

Empty brackets for the plain build, net and signal once the tests are in.

What actix-server 2.8.0 is missing

actix-rt 2.14.0 moved its networking and signal code behind two features, net and signal. Both are on by default, and its changelog says anyone who turns default features off now has to ask for them by name. actix-server 2.8.0 turns them off and asks for neither:

[dependencies.actix-rt]
version = "2.10"
default-features = false

Its own code still imports actix_rt::net::TcpStream. Up to actix-rt 2.13 that worked, because the net module was always compiled. From 2.14 on, nothing in a plain build switched it on.

The fix

actix-server 2.9.5 names both features itself:

[dependencies.actix-rt]
version = "2.14"
features = ["net", "signal"]
default-features = false

actix-web 4.15 accepts any actix-server 2.x from 2.7 up, so moving to 2.9.5 needs no change to Cargo.toml. This cargo update pins it, and Cargo.lock is the only file that changes:

cargo update -p actix-server --precise 2.9.5

Check it worked

Build without the test targets, then look at the features again:

$ cargo build
$ cargo tree -e normal -i actix-rt --depth 0 -f '{p} [{f}]'
actix-rt v2.15.0 [net,signal]

net and signal now come from actix-server itself rather than from a test dependency.

Catching it next time

After a bump that only touches Cargo.lock, I run a plain cargo build as well as cargo test. Test targets pull dev-dependencies in, and a dev-dependency can quietly cover for a feature that a normal dependency forgot to ask for.

Back to all guides