GitLab 18 Upgrade Breaks Private NPM Package Pull on Shared Runners (403 Forbidden)

Problem to solve

Describe your question in as much detail as possible:

After upgrading our self-managed GitLab instance through versions 17.8.5 → 17.8.7 → 17.11.2 → 18.0.0, our build CI job now fails on npm install for private scoped packages with:

npm --production=false install
npm warn config production Use --omit=dev instead.
npm error code E403
npm error 403 403 Forbidden - GET https://gitlab.example.com/api/v4/packages/npm/@chaptesi%2fmy-library
npm error 403 In most cases, you or one of your dependencies are requesting
npm error 403 a package version that is forbidden by your security policy, or
npm error 403 on a server you do not have access to.
npm error A complete log of this run can be found in: /builds/flashcake/my-project/.npm/_logs/2025-05-19T12_48_18_316Z-debug-0.log

This was working prior to the upgrade. We expect npm install to authenticate with ${CI_JOB_TOKEN} and pull our private packages, not produce a 403.

Here’s my current .npmrc file:

# Set URL for scoped packages.
@chaptesi:registry=https://gitlab.example.com/api/v4/packages/npm/

# Create an environment variable for your GitLab auth token
'//gitlab.example.com/api/v4/packages/npm/:_authToken'="${GITLAB_AUTH_TOKEN}"

'//gitlab.example.com/api/v4/projects/324/packages/npm/:_authToken'="${GITLAB_AUTH_TOKEN}"

Steps to reproduce

  1. Create a new repository (e.g. demo-npm-403 ) and add a simple package.json :
{
  "name": "demo-npm-403",
  "version": "1.0.0",
  "private": true,
  "dependencies": {
    "@chaptesi/my-library": "1.2.3"
  }
}
  1. Add a .npmrc at the project root and replace the PROJECT_ID:
# Set URL for scoped packages.
@chaptesi:registry=https://gitlab.example.com/api/v4/packages/npm/

# Create an environment variable for your GitLab auth token
'//gitlab.example.com/api/v4/packages/npm/:_authToken'="${GITLAB_AUTH_TOKEN}"

'//gitlab.example.com/api/v4/projects/<PROJECT_ID>/packages/npm/:_authToken'="${GITLAB_AUTH_TOKEN}"
  1. Push these files to your GitLab repo and configure the CI/CD pipeline with a job that runs npm install . Example .gitlab-ci.yml :
build:
    stage: build
    variables:
        GITLAB_AUTH_TOKEN: '$CI_JOB_TOKEN'
    script:
        - npm install
  1. Run the pipeline. On GitLab 18.0.0 (self-managed), you should see the same 403 Forbidden error fetching the scoped package.

Which troubleshooting steps have you already taken? Can you link to any docs or other resources so we know where you have been?

  • Confirmed my user has Maintainer role at both group and project levels and can access the Package Registry.
  • Checked Settings > CI/CD > Job token permissions and verified that both projects are included in the job token allowlist, flashcake/my-project and chaptesi/my-library.
  • Included the group of the library (chaptesi) to the Job token permissions
  • Ensured the GitLab Runner instance (“shared runners” host) is configured with the read_registry and write_registry scopes so it can pull and push packages.

Versions

Please select whether options apply, and add the version information.

  • Self-managed
  • GitLab.com SaaS
  • Dedicated
  • Self-hosted Runners

Versions

  • GitLab: 18.0.0
  • GitLab Runner: 18.0.1

We have the same problem on version 18.0.1. Job tokens just stopped working properly. Any suggestions how to fix that?

As a GitLab admin, I had to disable the Job token permissions feature from the Admin Area to get things working. I went to Admin Area > Settings > CI/CD > Job token permissions and unchecked Enable and enforce job token allowlist for all projects

Thanks a lot! It helped. In our case core of the problem was that we’re using a group package registry, but group settings hasn’t job tokens parameters, but they’re definitely used when you trying to have access to you group registry using a job token.

It’s Deprecations and removals by version | GitLab Docs
This is a breaking change in GitLab 18.
GitLab 18.0 released with GitLab Duo for Premium and Ultimate | GitLab

See → Settings → CI/CD → Job token permissions