Skip to content
Latchkey

Cargo "failed to authenticate" - Private Registry Auth in CI

Cargo tried to fetch a crate from a private registry (or a private git source) and the credentials were missing or rejected. Without a valid token the registry returns 401 and resolution fails.

What this error means

cargo fails with failed to authenticate, 401 Unauthorized, or no token found for registry while updating or downloading from a non-crates.io source. It happens in CI even when it works locally, because the local credential isn’t present on the runner.

cargo output
error: failed to download from `https://crates.mycompany.com/api/v1/crates/internal/1.0.0/download`

Caused by:
  failed to get successful HTTP response from ... 401 Unauthorized

Common causes

No registry token on the runner

The private registry needs a token that exists in your local ~/.cargo/credentials.toml but was never provided to CI as a secret/env var.

Wrong or expired credentials

The token is present but invalid, expired, or scoped to the wrong registry, so the server rejects it with 401.

How to fix it

Provide the token via an environment variable

Cargo reads a per-registry token from a CARGO_REGISTRIES_<NAME>_TOKEN env var - supply it from a CI secret.

.github/workflows/ci.yml
# registry named "my-registry" in .cargo/config.toml
export CARGO_REGISTRIES_MY_REGISTRY_TOKEN="${{ secrets.CARGO_REGISTRY_TOKEN }}"
cargo build --locked

Declare the registry source

Make sure the registry index is configured so Cargo knows where to authenticate.

.cargo/config.toml
# .cargo/config.toml
[registries.my-registry]
index = "sparse+https://crates.mycompany.com/index/"

How to prevent it

  • Inject registry tokens as CI secrets via CARGO_REGISTRIES_<NAME>_TOKEN.
  • Rotate and scope tokens narrowly to the registries that need them.
  • Keep credentials.toml out of version control.

Related guides

Tired of flaky CI? Latchkey auto-heals failed jobs and retries them for you. Start free →