NuGet private feed "401 Unauthorized" (PAT in nuget.config) in CI
The feed is reachable but rejected the request for credentials. A 401 means no valid personal access token (PAT) was presented; the fix is to inject a token into a feed credential entry, not to change the package reference.
What this error means
dotnet restore fails with "Unable to load the service index" or "Response status code does not indicate success: 401 (Unauthorized)" for a private feed, while nuget.org packages restore fine.
error : Unable to load the service index for source https://pkgs.dev.azure.com/org/_packaging/feed/nuget/v3/index.json.
error : Response status code does not indicate success: 401 (Unauthorized).Common causes
No credentials configured for the feed
The nuget.config lists the source but has no packageSourceCredentials, so restore sends an anonymous request and is rejected with 401.
An expired or wrong-scope PAT
A token is present but expired, or lacks Packaging read scope, so the feed refuses it.
How to fix it
Add credentials from a CI secret
- Store the PAT as a CI secret, never in committed config.
- Add a
packageSourceCredentialsentry that reads the secret via env substitution. - Re-run restore and confirm the 401 clears.
<packageSourceCredentials>
<contoso>
<add key="Username" value="ci" />
<add key="ClearTextPassword" value="%NUGET_PAT%" />
</contoso>
</packageSourceCredentials>Inject the token in the workflow
Map the secret into the step env so the config substitution resolves at restore time.
- run: dotnet restore
env:
NUGET_PAT: ${{ secrets.NUGET_PAT }}How to prevent it
- Keep feed PATs in CI secrets, never in committed
nuget.config. - Grant least-privilege Packaging read scope to the token.
- Rotate tokens on a schedule and update the secret in one place.