Skip to content
Guides

Why a package install returns 403, and what to do

A 403 from npm, pip or dotnet inside CI usually means a policy said no. How to tell policy from authentication, read the refusal, and fix the right thing.

Updated

A build that installed cleanly yesterday fails today with 403 Forbidden from the package manager, naming a package and a version and not much else. Before rotating a token or just rerunning the pipeline, it's worth thirty seconds to work out which of two very different things happened.

Policy or authentication, the status code tells you first

Dependably Packages answers a missing, expired, or wrong-organization token with 401, not 403. A 401 is the token, and the fix is the token, not the package. A 403 means the registry understood the request and a rule your organization configured refused that version, so only that package fails while everything else in the same build resolves normally. Some other registries send 403 for a bad token too, and there breadth is the tell: a token problem fails every request from the moment the token changed.

What a policy refusal should tell you

A registry enforcing policy should say which rule fired. Dependably Packages names it in an X-Dependably-Block-Reason response header on npm, PyPI, NuGet and RPM downloads and on Docker images refused by the licence policy, and writes every refusal to the audit log in the web UI. The reasons include a licence that isn't on the allowed list, a version named by a malicious-package advisory, a CVSS score above your tolerance, a package on the CISA Known Exploited Vulnerabilities Catalog, a release younger than the configured minimum age, an install script the policy doesn't allow, and a version an Admin or Owner blocked by hand.

Each of those calls for a different response. A licence problem means either the licence is genuinely fine and the allow list needs updating, or the package needs replacing. Do not widen the list only to make the build pass. An advisory or KEV listing means moving to a fixed version if one exists. If none does, an Admin or Owner can unblock that one version from its Actions menu on the package page. Unblock is recorded in the audit log but overrides every gate and does not expire, so write down why and revisit it when a fix ships. Never unblock a malicious or manual block without asking who set it and why. A minimum-age refusal usually means waiting it out, since the rule exists because a compromised publish is normally noticed within days. Pinning the previous version is the standard workaround in the meantime. An install-script refusal is worth actually reading before overriding: most packages that need one are build tools with native code, and most that don't need one but ship one anyway are worth a second look.

A name that isn't yours can still cause a refusal

If an internal package name isn't published anywhere, a public package with the same name can win dependency resolution and land in a build with no error at all, which is the quiet and more dangerous version of this problem. Reserving the name, or the whole scope, in the registry closes that gap: a reserved name never consults the public registry, so a squatted upstream package is invisible to your builds and only what your organization publishes under that name resolves. If a package that turns out to be one of yours gets refused or silently substituted, the fix is reserving the name and publishing the internal version, not opening up the public one.

Habits that make the next one faster

Print the response headers in CI, not only the status code. Package managers report the status and drop the headers, so the refusal reason never reaches the build log unless you ask for it: curl -sS -o /dev/null -D - -u user:<your token> <package URL> shows it. And treat a policy 403 as something to look at, not something to retry: a rerun that only succeeds because someone quietly widened a rule is exactly the outcome the rule existed to prevent.

Where this comes up in the products

The pages that describe how Dependably Packages and Dependably StatusCheck handle the problem this guide walks through.