Search code examples
node.jsnode.js-fs

Node 20 error: `EACCES: permission denied` on `fs.writeFileSync` to file in TMPDIR


The bug.mjs: open /tmp/test/foo.json, read and append k-v to save.

Update: It works with Node 18, but failed with Node 20/21:

import fs from 'fs'
import path from 'path'
import os from 'os'

function modify(fsPath, key, value) {
  let dir = path.dirname(fsPath)
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true })
  let content = fs.readFileSync(fsPath, { encoding: 'utf8', flag: 'a+' })
  content = content || '{}'
  content = JSON.parse(content)
  content[key] = value
  content = JSON.stringify(content)
  fs.writeFileSync(fsPath, content, { encoding: 'utf8' })
}

const p = path.join(os.tmpdir(),  'foo.json')
modify(p, 'key', 'foobar')

node bug.mjs:

Error: EACCES: permission denied, open '/var/folders/7q/r_8_1r5d5bb0dfycljk5pr1h0000gn/T/test/foo.json'
    at Object.writeFileSync (node:fs:2342:20)
    at modify (file:///Users/fannheyward/bug.mjs:13:6)
    at file:///Users/fannheyward/bug.mjs:17:1
    at ModuleJob.run (node:internal/modules/esm/module_job:222:25)
    at async ModuleLoader.import (node:internal/modules/esm/loader:323:24)
    at async loadESM (node:internal/process/esm_loader:28:7)
    at async handleMainPromise (node:internal/modules/run_main:120:12) {
  errno: -13,
  code: 'EACCES',
  syscall: 'open',
  path: '/var/folders/7q/r_8_1r5d5bb0dfycljk5pr1h0000gn/T/test/foo.json'
}

The /var/folders/7q/r_8_1r5d5bb0dfycljk5pr1h0000gn/T/test/foo.json is created but with no permission:

----------  1 fannheyward  staff     0B  3 14 09:31 /var/folders/7q/r_8_1r5d5bb0dfycljk5pr1h0000gn/T/test/foo.json

Update 2: the folder is accessible

drwxr-xr-x@  3 fannheyward  staff    96B  3 14 09:42 test/

Update 3: both Node 18 and 20 are installed with brew:

❯ /opt/homebrew/opt/node@18/bin/node --version
v18.19.1

❯ /opt/homebrew/opt/node@20/bin/node --version
v20.11.1

Solution

  • Update: this has been fixed with https://github.com/nodejs/node/pull/52101, and released in v20.13.0 and v22.0.0.

    For preview versions: use fs.readFileSync(fsPath, { flag: 'a+' }).toString('utf8')

    Some links: