Publishing a tarball you packed yourself fails with EALLOWFILE on every released npm 12 when allow-file is set to none or root. This page gives the config lookup that finds the setting, an auth-free reproduction, a one-flag fix that keeps the policy in place, and the test that separates this bug from a genuine file: dependency being blocked.

What breaks, and on which npm versions

The failure shows up in a split release pipeline: npm pack runs in a low-privilege job, npm publish of the produced artifact runs in a privileged one, and the privileged job rejects its own input.

npm error code EALLOWFILE
npm error Fetching packages of type "file" have been disabled

Both lines come from npm/cli issue #10144, filed 20 October 2026 against npm 12.2.0 on Node 26.3.0. The reporter confirms it on 12.2.1 as well, the current release as of 14 October 2026.

What you need to hit it:

  • npm 12.x, on Node ^22.22.2 || ^24.15.0 || >=26.0.0, the engine range npm 12.0.0 requires.
  • allow-file set to none or root somewhere in the config chain.
  • A positional tarball argument: npm publish ./dist/pkg-1.0.0.tgz or npm pack ./dist/pkg-1.0.0.tgz.

Nobody lands here by accident. Per the npm 12 config docs, allow-file and allow-directory both still default to all; only allow-git and allow-remote flipped to none in npm 12, which GitHub's 24 June 2026 changelog states explicitly. You get EALLOWFILE because somebody on your team hardened all four knobs past the defaults, the same instinct that produces the four distinct causes behind EALLOWGIT and patch-package exiting with status 1 under EALLOWREMOTE.

Target you pass to publishspec typeBlocked by a hardened policy?Artifact
nothing, or .directoryno, since npm 12.1.0 (21 Sep 2026)PR #9756, closing #9755
./packages/foodirectoryno, same exemption; backported to npm 11 as #9916PR #9916
./dist/pkg-1.0.0.tgzfileyes, on every released 12.x and 11.x#10144, fix open in #10145

What I read before writing this: issue #10144 and PR #10145, both open and unmerged on 21 October 2026, and PR #9756, merged into 12.1.0 one month earlier. The directory case and the file case are the same report with one word changed, filed a month apart.

Why a tarball you just packed counts as a dependency fetch

allow-file governs pacote fetching a file: spec, which is how a dependency pointing at a local tarball or folder gets installed. The gate lives in pacote's canUse() helper and fires on spec type, with no idea whether the spec arrived from a lockfile or from argv.

npm pack and npm publish take a positional target, hand it to npa(), and get back a spec whose type is directory, remote, or file. PR #9756 taught the directory path that an explicit target is the package being published rather than something being resolved, by forcing the policy open for that one call in libnpmpack/lib/index.js:

// An explicit directory is the package being packed, not a dependency fetch.
if (spec.type === 'directory') {
  opts = { ...opts, allowDirectory: 'all' }
}

Registry specs get equivalent treatment further down the same file, where the tarball fetch sets allowRemote: 'all'. Three spec types reach pack, two carry an exemption, file carries none.

npm publish ./dist/pkg-1.0.0.tgznpa parses the positional targetspec.typeallowDirectory forced to all by PR 9756allowRemote forced to all for the tarball fetchno override appliedpack or publish proceedspacote canUse evaluates allow-filethrows EALLOWFILEdirectoryremotefile

The root setting deserves its own paragraph, because it is the value a careful reader picks first and it cannot work here. In lib/commands/publish.js the manifest is read with pacote.manifest(spec, { ...opts, fullMetadata: true, fullReadJson: true }) and no _isRoot flag, while the later pacote.packument() call does pass _isRoot: true. The escape hatch inside canUse() reads if (allow !== 'none' && isRoot). An unflagged spec can never satisfy it, so allow-file=root rejects your own package and says so using npm 12's wording for a non-root spec:

npm error code EALLOWFILE
npm error Fetching non-root packages of type "file" have been disabled

Reproduce it in thirty seconds, no registry needed

1. Find out where allow-file is set. The error text names no config key and no package, so start by locating the line.

npm --version
npm config get allow-file
npm config get allow-file --location=project
npm config get allow-file --location=user
npm config get allow-file --location=global

A runner with a baked-in /usr/etc/npmrc prints all at project level and none at global level, which is the arrangement that sends people looking in the repo and finding nothing.

2. Reproduce with npm pack, not npm publish. pack takes the identical positional target and needs no auth or registry, so it isolates the policy gate from every credential problem. PR #10145 adds exactly this as a regression test, for none and for root.

mkdir /tmp/eallowfile && cd /tmp/eallowfile
npm init -y
npm pack
npm pack ./eallowfile-1.0.0.tgz --dry-run --allow-file=none

The fourth command prints the two error lines quoted above and exits non-zero.

3. Confirm the directory path is exempt. This is the control that proves the problem is argument classification and not your tarball.

npm pack . --dry-run --allow-directory=none

That one succeeds, under a policy value just as strict, because of the spec.type === 'directory' branch merged in #9756.

The fix that does not widen your policy

4. Scope the override to the single invocation.

npm publish ./dist/my-pkg-1.0.0.tgz --allow-file=all

For a runner where threading a flag through a wrapper script is awkward, the environment form does the same thing for one process:

npm_config_allow_file=all npm publish ./dist/my-pkg-1.0.0.tgz

Publishing a prebuilt tarball resolves no dependency graph, so opening allowFile for that process grants nothing a file: dependency could use. Writing it into ~/.npmrc is a different decision, and it persists into every npm install that runner performs afterwards. npm's maintainers made the same call about scope when they stopped persistent allow-scripts values from leaking into child installs, covered here in the three ways the npm allow-scripts gate stays green.

5. Derive the tarball path instead of globbing it. Any pipeline matching dist/*.tgz publishes the wrong file the first time two artifacts land in that directory. libnpmpack builds the name from the manifest as <name>-<version>.tgz, stripping a leading @ and replacing / with -:

npm pack --pack-destination ./dist
TARBALL="./dist/$(node -p "const p=require('./package.json'); p.name.replace(/^@/,'').replace(/\//g,'-')+'-'+p.version").tgz"
npm publish "$TARBALL" --allow-file=all

That assumes one package per job. In a workspace release, run npm pack --json per workspace and read the filename out of its output rather than reconstructing it.

6. Verify the override worked and did not leak.

npm pack ./dist/my-pkg-1.0.0.tgz --dry-run --allow-file=all
npm config get allow-file

The first command prints the tarball filename and exits 0. The second must still print none or root; if it prints all, somebody persisted the value and the policy is gone. On the publish side, npm publish --dry-run --allow-file=all gets past the policy gate and stops at authentication instead, which in #10144 surfaces as a warning that login is required. Reaching an auth complaint is the success signal, since it means the spec was classified and accepted.

Is this the publish bug, or a real file: dependency?

Same error code, same message, two unrelated causes, and the message names no package to tell them apart. Use the command that printed it:

  • npm pack or npm publish with a positional .tgz: the bug in this article. Override on the command.
  • npm install or npm ci: the gate working as designed on a file: spec somewhere in the tree. The fix belongs in the dependency graph, by declaring that package at the root or vendoring it. Do not touch the publish job.

Two more traps worth naming. Dropping the positional argument and publishing from the directory does clear the error, because a directory spec has been exempt since 12.1.0, and it also collapses the privilege split the two-job pipeline was built for, which is the change #10144 asks npm to avoid forcing. And npm 11 is not a refuge: the directory exemption was backported in #9916, the file case was not.

FAQ

Is there a released npm that fixes this? No. As of 21 October 2026 the newest release is 12.2.1 and PR #10145 is open and unmerged. Treat that as a live claim and re-check the PR before concluding your version is clear.

Why does allow-file=root fail on my own package? The publish manifest fetch passes no _isRoot flag, and pacote's canUse() requires isRoot to be true before honouring root. The setting cannot see your package as root.

Does --allow-file=all on publish weaken anything? For a prebuilt tarball, no dependency resolution happens in that process, so there is nothing for the relaxed value to permit. The exposure comes from persisting it in .npmrc, where it applies to installs.

Can I keep the two-job pipeline? Yes, with the per-command override. The alternative, publishing from a checkout in the privileged job, removes the separation between the job that runs build scripts and the job that holds the publish token.

How do I confirm my runner is actually affected before changing the pipeline? Run step 2 in a scratch directory on the runner. It needs no registry, no token, and no network.

What goes in the release job today

Keep allow-file=none (or root) in committed config, add --allow-file=all to the one npm publish line, compute the tarball path from package.json or npm pack --json, and subscribe to #10145 so the override comes out when the fix ships. Then audit the other three allow-* knobs the same way, using the npm 12 rollout breakages that only appear after CI goes green as the map, and the OIDC publish failure where your sub claim changed as the other thing that stops a hardened release job at the last step.