Skip to content

jwadolowski/pnpm-tsx-pipe-einval

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

pnpm-tsx-pipe-einval

Reproduces the EINVAL error triggered when tsx builds a UNIX socket path that is too long for Linux, caused by pnpm overriding TMPDIR while running a git-dep prepare script as root.

Root cause analysis

1. Linux UNIX socket path limit

Linux limits UNIX domain socket paths to 108 bytes (the sun_path field of sockaddr_un includes the NUL terminator, leaving 107 usable chars). listen() returns EINVAL when the path exceeds this limit.

2. tsx IPC socket path construction

tsx creates an IPC server for inter-process communication when running scripts. The socket path is assembled as follows:

src/utils/temporary-directory.ts:

export const tmpdir = path.join(os.tmpdir(), `tsx-${userId}`);
// e.g. /tmp/tsx-0

src/utils/ipc/get-pipe-path.ts:

const pipePath = path.join(tmpdir, `${processId}.pipe`);
// e.g. /tmp/tsx-0/<pid>.pipe

os.tmpdir() respects the TMPDIR environment variable.

3. @pnpm/npm-lifecycle sets TMPDIR for root

When pnpm runs lifecycle scripts as root (uid=0), unsafe-perm defaults to false. @pnpm/npm-lifecycle then creates <wd>/node_modules/.tmp/ and sets TMPDIR:

if (!opts.unsafePerm) {
  const tmpdir = path.join(wd, 'node_modules', '.tmp')
  try {
    fs.mkdirSync(tmpdir, { recursive: true })
  } catch (err) {
    if (err.code !== 'EEXIST') throw err
  }
  env.TMPDIR = tmpdir
}

<wd> is pnpm's git-dep extraction directory inside the store. The resulting TMPDIR becomes quite long:

/root/.local/share/pnpm/store/v11/tmp/_tmp_7_<hash>/node_modules/.tmp

Combined with the tsx socket suffix, the full path lands at 110 chars (exceeds the 107-char limit):

/root/.local/share/pnpm/store/v11/tmp/_tmp_7_<hash>/node_modules/.tmp/tsx-0/<pid>.pipe

When running as a non-root user (uid=1000), unsafe-perm defaults to true - no TMPDIR override. tsx uses os.tmpdir()=/tmp and the socket path stays below the 108-byte limit.

Steps to reproduce

# Succeeds: USER node skips the TMPDIR override (unsafe-perm=true by default)
docker build -f Dockerfile.node -t tsx-node --no-cache .

# Fails with EINVAL (root + long TMPDIR)
docker build -f Dockerfile.root -t tsx-root --no-cache .

# Succeeds: root + short store path keeps the socket path under the limit
docker build -f Dockerfile.root-pnpm-store -t tsx-root-pnpm-store --no-cache .

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors