Find unmaintained packages in Rust projects
cargo-unmaintained is similar to cargo-audit. However, cargo-unmaintained finds unmaintained packages automatically using heuristics, rather than rely on users to manually submit them to the RustSec Advisory Database.
cargo-unmaintained defines an unmaintained package X as one that satisfies one of 1 through 3 below:
-
X's repository is archived (see Notes below).
-
X is not a member of its named repository.
-
Both a and b below.
a. X depends on a package Y whose latest version:
- is incompatible with the version that X depends on
- was released over a year ago (a configurable value)
b. Either X has no associated repository, or its repository's last commit was over a year ago (a configurable value).
As of 2026-08-28, the RustSec Advisory Database contains 244 active advisories for unmaintained packages. Using the above conditions, cargo-unmaintained automatically identifies 150 (61%) of them. These results can be reproduced by running the rustsec_advisories example within this repository.
-
To check whether packages' repositories have been archived, set the
GITHUB_TOKEN_PATHenvironment variable to the path of a file containing a personal access token. If unset, this check will be skipped. -
The above conditions consider a "leaf" package (i.e., a package with no dependencies) unmaintained only if conditions 1 or 2 apply.
-
The purpose of the "over a year ago" qualifications in condition 3 is to give package maintainers a chance to update their packages. That is, an incompatible upgrade to one of X's dependencies could require time-consuming changes to X. Without this check,
cargo-unmaintainedwould produce many false positives.
- Of the 94 packages in the RustSec Advisory Database not identified by
cargo-unmaintained:- 14 do not build
- 6 are existent, unarchived leaves
- 15 were updated within the past 365 days
- 59 were not identified for other reasons
cargo-unmaintained's output includes the number of days since a package's repository was last updated, along with the dependencies that cause the package to be considered unmaintained.
For example, the following is the output produced by running cargo-unmaintained on Cargo 0.74.0 on 2023-11-11:
cargo install cargo-unmaintainedUsage: cargo unmaintained [OPTIONS]
Options:
--all-targets Whether all targets should be considered, rather than just the host
--color <WHEN> When to use color: always, auto, or never [default: auto]
--fail-fast Exit as soon as an unmaintained package is found
--json Output JSON (experimental)
--max-age <DAYS> Age in days that a repository's last commit must not exceed for the
repository to be considered current; 0 effectively disables this check,
though ages are still reported [default: 365]
--no-cache Do not cache data on disk for future runs
--no-exit-code Do not set exit status when unmaintained packages are found
--no-warnings Do not show warnings
-p, --package <NAME> Check only whether package NAME is unmaintained
--purge Remove all cached data from disk and exit
--save-token Read a personal access token from standard input and save it to
$HOME/.config/cargo-unmaintained/token.txt
--tree Show paths to unmaintained packages
--verbose Show information about what cargo-unmaintained is doing
-h, --help Print help
-V, --version Print version
The `GITHUB_TOKEN_PATH` environment variable can be set to the path of a file containing a personal
access token. If set, cargo-unmaintained will use this token to authenticate to GitHub and check
whether packages' repositories have been archived.
Alternatively, the `GITHUB_TOKEN` environment variable can be set to a personal access token.
However, use of `GITHUB_TOKEN_PATH` is recommended as it is less likely to leak the token.
If neither `GITHUB_TOKEN_PATH` nor `GITHUB_TOKEN` is set, but a file exists at
$HOME/.config/cargo-unmaintained/token.txt, cargo-unmaintained will use that file's contents as a
personal access token.
Unless --no-exit-code is passed, the exit status is 0 if no unmaintained packages were found and no
irrecoverable errors occurred, 1 if unmaintained packages were found, and 2 if an irrecoverable
error occurred.
If a workspace's Cargo.toml file includes a workspace.metadata.unmaintained.ignore array, all packages named therein will be ignored. Example:
[workspace.metadata.unmaintained]
ignore = ["matchers"]Running just cargo test will not run the "externally influenced" tests or the RustSec tests. To run those additional tests, add --features=test-ei and --features=test-rustsec, respectively, i.e.:
cargo test --features=test-ei
cargo test --features=test-rustsecThe ci integration test runs the tests in the ci package from the workspace root. To run just one of those tests, set FILTER to its name (or another substring accepted by the Rust test harness). For example, the following runs only the supply-chain check:
FILTER=supply_chain cargo test --test ciThe available test names can be listed with cargo test -p ci -- --list. A command-line filter passed directly to cargo test --test ci would filter the outer integration test instead of the ci package tests that it runs.
-
If a package is renamed from X to Y, it is immediately considered unmaintained because the package's repository no longer contains a package named X. (#441)
Discussion
I (@smoelius) suspect there may be no good solution to this problem.
PRs #575 and #613 explored the possibility of finding a package Y with the same properties package X but a different name. However, arbitrary changes could be made to a package before its name is changed. This fact complicates such package matching.
The
toml_writepackage provides an example. Version 0.1.2 was published at commit 838a022 (2025-06-06). With commit 8658e70 (2025-06-12), the keywordno_stdwas added to itsCargo.tomlfile. Finally, with commit b3594df (2025-07-07),toml_writewas renamed totoml_writer. Thus, the publishedtoml_writepackage does not match the repository'stoml_writerpackage because the latter's keywords includeno_std.The recommended workaround is to use an
ignoreconfiguration. If the renamed package really is a false positive, it should be published shortly after it was renamed. Thus, such a workaround should not need to remain in place for long. -
If a project relies on an old version of a package,
cargo-unmaintainedmay fail to flag the package as unmaintained (i.e., may produce a false negative).Discussion
The following is a sketch of how this can occur.
- The project relies on version 1 of package X, which has no dependencies.
- Version 2 of package X exists, and adds version 1 of package Y as a dependency.
- Version 2 of package Y exists.
Note that version 1 of package X appears maintained, but version 2 does not. Ignoring a few details, version 2 satisfies condition 3 above.
cargo-unmaintaineddoes not, in all cases, check whether the latest version of a package is used, as doing so would be cost prohibitive. A downside of this choice is that false negatives can result.Note that false positives should not arise in a corresponding way. Before flagging a package as unmaintained,
cargo-unmaintainedverifies that the package's latest version would be considered unmaintained as well.
-
Yesterday, I got a warning about an unmaintained package. But, today, I don't. Why is that?
Possibly, an intermediate dependency was updated. Suppose package X depends on Y, which depends on Z. And suppose Z is considered unmaintained. Then Z will generate warnings for both X and Y. If Y is updated to no longer depend upon Z, and X uses the new version of Y, then X will no longer receive warnings about Z.
cargo-unmaintained is not meant to be a replacement for cargo-upgrade. cargo-unmaintained should not warn just because a package needs to be upgraded.
We reserve the right to change the following and to consider such changes non-breaking:
- what data is stored in the cache, as well as how that data is stored
- the output produced by the experimental
--jsonoption
cargo-unmaintained is licensed and distributed under the AGPLv3 license. Contact us if you're looking for an exception to the terms.
