Skip to content
Latchkey

Artifactory pip "401" (index-url token) in CI

pip reached the Artifactory PyPI index but was rejected with 401 because the index-url carried no credentials, or the embedded token is wrong or expired.

What this error means

A pip install against the Artifactory index fails with "401 Client Error: Unauthorized for url: https://acme.jfrog.io/artifactory/api/pypi/pypi-virtual/simple/...".

pip
  ERROR: HTTP error 401 Client Error: Unauthorized for url:
  https://acme.jfrog.io/artifactory/api/pypi/pypi-virtual/simple/requests/
ERROR: Could not find a version that satisfies the requirement requests

Common causes

The index-url has no embedded credentials

pip sends an anonymous request because the token was never embedded in the URL or set via PIP_INDEX_URL from a secret.

The embedded token is wrong or expired

A hardcoded or stale token in the index URL is rejected, so the index returns 401 for every request.

How to fix it

Set PIP_INDEX_URL from a secret with the token

  1. Store the Artifactory access token as a CI secret.
  2. Build the index URL with the token in the step env, not in a committed file.
  3. Re-run pip and confirm the index resolves.
.github/workflows/ci.yml
env:
  PIP_INDEX_URL: https://${{ secrets.JF_USER }}:${{ secrets.JF_ACCESS_TOKEN }}@acme.jfrog.io/artifactory/api/pypi/pypi-virtual/simple

Let jf configure the pip repository

The jf CLI can write a pip.conf that points at the Artifactory index with auth injected.

Terminal
jf pip-config --repo-resolve pypi-virtual
jf pip install -r requirements.txt

How to prevent it

  • Set PIP_INDEX_URL from a secret at CI time, never commit the token.
  • Prefer jf pip-config so credentials are managed by the CLI.
  • Rotate the token and update the single secret it comes from.

Related guides

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