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.
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.
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-0src/utils/ipc/get-pipe-path.ts:
const pipePath = path.join(tmpdir, `${processId}.pipe`);
// e.g. /tmp/tsx-0/<pid>.pipeos.tmpdir() respects the TMPDIR environment variable.
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.
# 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 .