By the end of this you will have a committed .npmrc granting the narrowest git policy your tree actually needs, plus an offline lockfile query that names every git-sourced package before you grant anything. The reason to bother: allow-git=root is the answer circulating everywhere, and it clears exactly one of the four ways npm 12 raises EALLOWGIT.
Prerequisites
- npm 12.0.0 or later. Check with
npm --version. Version 12.0.0 landed 8 July 2026, and the overrides bug below is reported against 12.0.2. jq, for the lockfile and manifest queries.- A
package-lock.jsonin the repo. Every offline check here reads it. - Write access to the repo's
.npmrc, and to your CI environment if the policy has to ship there.
Before arguing with anyone about whether this is your commit, quote the release. The v12.0.0 notes say that allow-git and allow-remote now default to none, and that you set them to all or root to install git or user-supplied tarball-URL dependencies. GitHub gave notice of the flip on 9 June 2026 in its changelog post. The code change is one commit, caa3295, touching workspaces/config/lib/definitions/definitions.js and lib/utils/error-message.js. The same commit flipped allow-remote, which produces a separate failure with its own page on the same afternoon.
The word that picks your fix
Two real failures, from two repos:
npm error code EALLOWGIT
npm error Fetching packages of type "git" have been disabled
npm error Refusing to fetch "github:JuliusBrussee/caveman"
npm error code EALLOWGIT
npm error Fetching non-root packages of type "git" have been disabled
npm error Refusing to fetch 'ssh2@github:mscdex/ssh2#45522bb8723d8ecf459a1d222086e56074c11682'
The first comes from JuliusBrussee/caveman issue #698 on npm 12.0.0, where a README install command stopped working. The second comes from npm/cli #9984 on npm 12.0.2, filed 15 September 2026, where the reporter had already passed --allow-git=root. Identical error code, opposite next step.
| Cause | Error contains non-root | Where the spec lives | Fix that works |
|---|---|---|---|
| 1. Default flip, direct dep | no | root dependencies / devDependencies | allow-git=root |
| 2. Transitive git dep | yes | a dependency's own package.json | allow-git=all, or drop the dep |
3. Git spec in overrides | yes | root package.json overrides | allow-git=all (npm/cli #9984, open) |
| 4. Stale npm 11.x | yes, wrongly | root dependencies | upgrade npm past PR #9206 |
Cause 4 costs people an afternoon. npm/cli #9189, opened 3 April 2026 against npm 11.12.1, reported that allow-git=root rejected a plain root-level "left-pad": "github:left-pad/left-pad" using the non-root wording, and it was closed by PR #9206. If your spec is unambiguously root-level and npm still calls it non-root, read npm --version before touching any config.
Step-by-step
1. Name every git spec in the tree without fetching anything.
jq -r '.packages | to_entries[]
| select((.value.resolved // "") | startswith("git+"))
| "\(.key)\t\(.value.resolved)"' package-lock.json
Lockfile v3 records git-sourced packages with a resolved value beginning git+. This runs offline, in under a second, and returns transitive members too. Run it before you grant anything, because the grant is what hides the rest of the list from you.
2. Decide root versus non-root for each name.
jq '{deps: .dependencies, dev: .devDependencies, overrides: .overrides}' package.json
A name under dependencies or devDependencies is cause 1. A name appearing only under overrides, or nowhere in this output at all, is cause 2 or cause 3.
3. Grant the narrowest policy your case needs.
npm config set allow-git=root --location=project
--location=project writes the repo's .npmrc, so the policy travels with the code and shows up in a reviewer's diff instead of living in someone's home directory. The v12 config docs define root as allowing only git dependencies defined in your project's package.json to be fetched and installed.
4. If step 2 found a git spec under overrides, widen it and write down why.
# .npmrc
# allow-git=root does not cover `overrides` - npm/cli#9984, open as of npm 12.0.2.
# Narrow this back to `root` once that issue closes.
allow-git=all
No narrower legal value exists for this shape. The reporter in #9984 hit it running npm install ssh2-sftp-client --allow-git=root against an ssh2 override, and notes it also reproduces in monorepos where the overrides block sits in the workspace-root package.json while the direct dependency sits in a child package.
5. Apply the same policy in CI, where the repo .npmrc may not be read.
# GitHub Actions
env:
NPM_CONFIG_ALLOW_GIT: root
# any runner
npm_config_allow_git=all npm ci
Both spellings are in production use: the caveman maintainer shipped NPM_CONFIG_ALLOW_GIT in the project's install script, and nodejs/citgm PR #1133 uses the lowercase npm_config_allow_git to keep its npm 12 smoke tests installing from git.
Verify it works
Exit code 0 does not prove the tree is whole. The v12 config docs warn that these limits could leave your tree incomplete and that some packages may not function as intended or designed, which is a quieter failure than a red build.
npm config get allow-git
npm ci
npm query ':type(git)' | jq -r '.[].name' | sort -u
npm config get allow-git prints the effective value after every config layer resolves, which catches a typo and a setting written to the wrong .npmrc with the same command. npm query ':type(git)' is npm's documented selector for git-sourced nodes. Its output should match, name for name, the list step 1 pulled from the lockfile. A name present in step 1 and missing here means npm skipped that package rather than failing on it.
Then pin the count so a later install cannot silently shrink the tree:
npm query ':type(git)' --expect-result-count=1
Set the number to what step 1 returned. This is the line to drop into CI, and the one to delete the day you remove the last git spec from the graph.
Common pitfalls
A hostname is not a valid value. Guides in circulation suggest --allow-git=github.com. The documented type is all, none, or root. npm rejects anything else as invalid config and keeps the default, so the install fails again behind a warning most people scroll past. If npm config get allow-git prints none after you set a hostname, you have your answer.
Your git dep fails with the wrong error code. When the repo has a prepare script, npm builds it by spawning an inner npm install, and the child inherits npm_config_allow_scripts from your user-level .npmrc. The child then treats that inherited env value as a command-line policy and refuses:
npm error code EALLOWSCRIPTS
npm error --allow-scripts is not allowed in project-scoped installs.
That is npm/cli #9783, open since 18 July 2026 and reproduced on 11.17.0 and 12.0.1. The advice inside the error goes in a circle, since the setting already came from .npmrc. Unset allow-scripts in your user .npmrc and express approvals through the root package instead. The field layout is in the allowScripts schema reference, and the same error in its ordinary form is covered in the project-scoped install failure.
It was probably never your commit. eslint-plugin-import #3273 records the shape: last green run 14 July 2026, first failure 16 July, no dependency change. A runner image picked up npm 12, and a gist-hosted webpack spec in resolvers/webpack/package.json became unfetchable. Check the runner's npm version before bisecting your own history.
Wrap-up
Run the step 1 query across every repo in the estate rather than only the one that broke. It is offline and takes seconds per repo. Repos with zero git+ entries need no config at all, and leaving allow-git unset there is free hardening. Repos with a root-level spec get allow-git=root in a committed .npmrc. Repos forced to all by the overrides gap get the comment naming npm/cli #9984 plus a calendar reminder: once npm view npm version shows a release past the fix, flip back to root and confirm npm ci still returns the same npm query ':type(git)' list.
For cause 3, the durable move is to stop overriding with a git URL. Publish the fork to a private scope, and the git spec leaves the graph entirely, so no allow-* grant applies to it. Sequencing the rest of this release across an estate, including the parts that surface after the build turns green, is covered in what breaks after CI goes green.
Comments
Be the first to comment.