Self-service
Describe the bug
In a long-running process using the pnp linker an error like EISDIR: illegal operation on a directory, read can be emitted when a zip file is removed and replaced by a directory with the same name. For example some.zip is a zip file and is removed. It is then replaced with a directory called some.zip (with some contents) and a read is attempted on a subpath (e.g. some.zip/file). This appears to be due to a caching issue in the pnp FS code as it does not happen when there was never a file called some.zip (or some.zip was never accessed from the process).
To reproduce
Package.json:
{
"name": "yarn-pnp-zip-mount-cache-demo",
"version": "1.0.0",
"private": true,
"type": "module",
"packageManager": "yarn@4.18.0"
}
.yarnrc.yml:
enableGlobalCache: false
nodeLinker: pnp
npmMinimalAgeGate: 0
repro.mjs
// Run with
// node --require ./.pnp.cjs repro.mjs
import fs from 'node:fs'
import Path from 'node:path'
const dir = Path.join(import.meta.dirname, 'work')
fs.rmSync(dir, { recursive: true, force: true })
fs.mkdirSync(dir, { recursive: true })
const zipPath = Path.join(dir, 'demo.zip')
const nestedPath = Path.join(zipPath, 'nested.txt')
console.log('--- Step 1: create demo.zip as a plain (non-archive) file ---')
fs.writeFileSync(zipPath, 'just some ordinary file content, not a real zip')
console.log('created:', zipPath)
console.log('\n--- Step 2: touch it with a plain fs call while it is a file ---')
fs.statSync(zipPath)
console.log('stat succeeded, isFile:', fs.statSync(zipPath).isFile())
console.log('\n--- Step 3: delete the file, replace it with a real directory ---')
fs.unlinkSync(zipPath)
fs.mkdirSync(zipPath)
console.log(
'demo.zip is now a directory:',
fs.statSync(zipPath).isDirectory()
)
console.log('\n--- Step 4: write a file nested under the new directory ---')
try {
fs.writeFileSync(nestedPath, 'hello from inside the real directory')
console.log('SUCCEEDED - wrote', nestedPath)
console.log('contents:', fs.readFileSync(nestedPath, 'utf8'))
} catch (err) {
console.log('FAILED:', err.message)
console.log('code:', err.code)
console.log(err.stack)
}
console.log('\n--- Control: same thing, but demo2.zip was not a file first ---')
const zipPath2 = Path.join(dir, 'demo2.zip')
fs.mkdirSync(zipPath2)
try {
fs.writeFileSync(Path.join(zipPath2, 'nested.txt'), 'hello')
console.log('SUCCEEDED (as expected - a directory that was always a directory works fine)')
} catch (err) {
console.log('FAILED (unexpected):', err.message)
}
Error appears in step 4 when run with node --require ./.pnp.cjs repro.mjs. No error when called with node repro.mjs
Environment
System:
OS: macOS 26.5.1
CPU: (11) arm64 Apple M3 Pro
Binaries:
Node: 24.14.1 - /private/var/folders/3f/5p9h4mx96q3gj7k8cc_l5qp80000gn/T/xfs-7194d65c/node
Yarn: 4.18.0 - /private/var/folders/3f/5p9h4mx96q3gj7k8cc_l5qp80000gn/T/xfs-7194d65c/yarn
npm: 11.11.0 - <my home>/.nvm/versions/node/v24.14.1/bin/npm
bun: 1.3.1 - <my home>/.bun/bin/bun
Deno: 2.1.6 - <my home>/.deno/bin/deno
Additional context
No response
Self-service
Describe the bug
In a long-running process using the
pnplinker an error likeEISDIR: illegal operation on a directory, readcan be emitted when a zip file is removed and replaced by a directory with the same name. For examplesome.zipis a zip file and is removed. It is then replaced with a directory calledsome.zip(with some contents) and a read is attempted on a subpath (e.g.some.zip/file). This appears to be due to a caching issue in the pnp FS code as it does not happen when there was never a file calledsome.zip(orsome.zipwas never accessed from the process).To reproduce
Package.json:
.yarnrc.yml:
repro.mjs
Error appears in step 4 when run with
node --require ./.pnp.cjs repro.mjs. No error when called withnode repro.mjsEnvironment
System: OS: macOS 26.5.1 CPU: (11) arm64 Apple M3 Pro Binaries: Node: 24.14.1 - /private/var/folders/3f/5p9h4mx96q3gj7k8cc_l5qp80000gn/T/xfs-7194d65c/node Yarn: 4.18.0 - /private/var/folders/3f/5p9h4mx96q3gj7k8cc_l5qp80000gn/T/xfs-7194d65c/yarn npm: 11.11.0 - <my home>/.nvm/versions/node/v24.14.1/bin/npm bun: 1.3.1 - <my home>/.bun/bin/bun Deno: 2.1.6 - <my home>/.deno/bin/denoAdditional context
No response