When Cargo can't find what you asked for
You run cargo build. You expect a binary. Instead, the terminal prints error: no matching package named X found. You check your Cargo.toml. The name looks correct. The version looks reasonable. Cargo disagrees.
This error is Cargo's way of saying your dependency query returned zero results. Cargo does not guess. It does not autocorrect typos. It queries the registry index, matches your constraints against the available data, and fails fast if nothing matches. The fix is almost always a mismatch between your specification and reality.
The resolver and the registry
Cargo is a dependency resolver. Your Cargo.toml contains a list of requirements. Each requirement has a name, a version constraint, and a source. The source defaults to crates.io, the public registry.
When you run a build, Cargo fetches the registry index. The index is a database of every published crate name and version. Cargo runs your requirements against this database. If a requirement matches no rows, you get the "no matching package" error.
The match depends on three things:
- The name must exist in the index.
- The version constraint must allow at least one non-yanked version.
- The source must be reachable and contain the crate.
Convention aside: The community treats Cargo.toml as a contract. If you write a dependency, you are asserting that it exists and is usable. Cargo enforces this contract literally. Read the error message carefully. It tells you exactly which package and version constraint failed. Copy that string. Paste it into the crates.io search. If the search returns nothing, the problem is the string.
Minimal example: Typos and exact versions
The most common cause is a typo. A single character off and the name vanishes from the index.
[dependencies]
# Typo in the name. 'serd' does not exist.
serd = "1.0"
Cargo queries the index for serd. The index has serde. The query returns zero results. Cargo errors.
Exact version constraints cause a different class of failure. The = operator pins a specific version. If that version does not exist, or if it has been yanked, the resolution fails.
[dependencies]
# Pins exactly version 1.0.0.
# Fails if 1.0.0 was never published or was yanked.
serde = "=1.0.0"
Cargo checks the index for serde version 1.0.0. If the maintainer yanked 1.0.0 due to a critical bug, the version disappears from the index for new resolutions. Cargo finds nothing. The build stops.
Use a version range like "1.0" instead. This translates to >=1.0.0, <2.0.0. Cargo finds the latest compatible version, skipping yanked releases.
Check the spelling. Then check the spelling again.
Yanked versions and the registry index
Yanking is a registry feature. When a maintainer yanks a version, that version is removed from the index. Existing projects with a Cargo.lock file keep working because the lock file records the exact version. New projects or projects that run cargo update cannot resolve the yanked version.
This behavior protects the ecosystem. It prevents new code from depending on broken releases. It also breaks exact version pins.
[dependencies]
# Safe range. Cargo picks 1.0.1 if 1.0.0 is yanked.
serde = "1.0"
# Dangerous pin. Fails if 1.0.0 is yanked.
serde = "=1.0.0"
The range "1.0" allows Cargo to skip 1.0.0 and select 1.0.1. The pin "=1.0.0" forces Cargo to use 1.0.0. If 1.0.0 is yanked, the pin fails.
Convention aside: The community avoids exact version pins in production code. Pins are useful for reproducible builds in CI or for debugging a regression. For day-to-day development, use ranges. Let semver handle updates.
Avoid exact version pins. They turn yanked releases into build breakers.
Realistic example: Git, path, and private registries
Not all dependencies come from crates.io. Cargo supports git repositories, local paths, and private registries. Each source has its own failure modes.
Git dependencies let you use code that hasn't been published. You specify a URL and a revision. If the revision doesn't exist, Cargo fails.
[dependencies]
# Git dependency with a specific revision.
# Fails if the rev is not found in the repository.
my-lib = { git = "https://github.com/user/repo", rev = "abc1234" }
Cargo clones the repository. It checks for abc1234. If the commit doesn't exist, or if the branch was deleted, the resolution fails. The error message mentions the git source.
Path dependencies point to local directories. They are used for workspace development. If the path is wrong, Cargo fails.
[dependencies]
# Path dependency.
# Fails if the directory does not exist or contains no Cargo.toml.
my-local-lib = { path = "../libs/my-local-lib" }
Cargo checks the filesystem. It looks for Cargo.toml in the target directory. If the path is relative and your working directory is wrong, the path resolves to nothing.
Private registries replace or supplement crates.io. You configure them in .cargo/config.toml. If you replace the default registry, you cut off access to public crates unless your private registry mirrors them.
# .cargo/config.toml
[source.crates-io]
replace-with = "private-registry"
[source.private-registry]
registry = "https://registry.example.com"
If private-registry does not contain serde, and you replace crates-io, Cargo cannot find serde. The error appears even though serde exists on the public internet.
Convention aside: Never replace crates-io unless you have a corporate proxy or a mirror. Replacing the default registry breaks the ecosystem. Use private-registry as an additional source, not a replacement.
Verify the remote repository exists and the revision is reachable.
Pitfalls: Network, index, and lock files
Network issues can masquerade as missing packages. If Cargo cannot fetch the index, it cannot resolve dependencies. The error might mention a network timeout, or it might fail with "no matching package" if the index is empty or corrupt.
Run cargo update to refresh the index. This forces Cargo to re-fetch the registry data. If the error persists, check your network connection and proxy settings.
The Cargo.lock file records the resolved versions. Sometimes the lock file becomes inconsistent with the registry. This happens if a version is yanked after the lock file was created. Running cargo build usually works because the lock file is authoritative. Running cargo update might fail because the yanked version is no longer available.
If cargo update fails, you can force Cargo to keep the lock file by not running update. Or you can manually edit the lock file to bump the version to a non-yanked release. This is rare. Usually, updating the constraint in Cargo.toml is easier.
Convention aside: Do not commit Cargo.lock for library crates. Commit it for binary crates. Libraries let downstream projects resolve dependencies. Binaries need reproducible builds. If you are writing a library and see this error, check your Cargo.toml constraints. If you are writing a binary, check your lock file and network.
Trust the error message. It tells you exactly what it couldn't find. Read the name and version in the error.
Decision: Choosing the right dependency spec
Use a caret range like "1.0" for standard dependencies. This allows automatic patch and minor updates while blocking breaking changes. It survives yanked versions.
Use an exact version "=1.0.0" only for reproducible builds or debugging. Accept that yanked versions will break the build.
Use git dependencies when the code is not published or you need unreleased changes. Verify the revision exists. Expect slower builds due to cloning.
Use path dependencies for local workspace development. Ensure the path is correct relative to the current crate.
Use cargo search when you are unsure of the crate name. The search returns matches from the registry.
Check your network connection when the error affects multiple packages. A network outage can make the registry appear empty.
Fix the spec. Cargo is literal.