Skip to content

Add random number to multishell symlinks#1536

Open
joliss wants to merge 1 commit intoSchniz:masterfrom
joliss:multishell-symlink
Open

Add random number to multishell symlinks#1536
joliss wants to merge 1 commit intoSchniz:masterfrom
joliss:multishell-symlink

Conversation

@joliss
Copy link
Copy Markdown
Contributor

@joliss joliss commented Mar 25, 2026

This protects against collisions when multiple shells are started in parallel inside Bubblewrap sandboxes, as each shell will have the same timestamp and the same low PID (e.g. 2), since Bubblewrap sandboxes have isolated PID spaces.

We're already using fastrand as a transitive dependency through tempfile.

This is the only place where fnm relies on std::process::id() for deconflicting.

How I actually ran into this

I'm loading fnm in my .bashrc even in non-interactive shells (i.e. before [ -z "$PS1" ] && return), so that Codex has access to fnm's node binary:

eval "$(XDG_RUNTIME_DIR=/tmp fnm env --shell=bash)"

With this, Codex in VS Code (via a VS Code remote into Linux) was hitting intermittent errors like Can't create the symlink for multishells at "/tmp/fnm_multishells/...". File exists.

Codex analysis Yes. The trigger is the `fnm` init at `.bashrc`:
if command -v fnm &>/dev/null; then
  # Set XDG_RUNTIME_DIR=/tmp to play nice with Codex sandbox.
  eval "$(XDG_RUNTIME_DIR=/tmp fnm env --shell=bash)"
fi

What I found:

  • Your /tmp override is necessary in this sandbox. Without it, fnm env --shell=bash tries /run/user/1000/fnm_multishells and fails because that path is read-only here.
  • The intermittent File exists is a separate issue. The tool’s default wrapper launches /bin/bash -c ..., and bash is auto-reading ~/.bashrc in this environment even for non-interactive commands. That makes the fnm line run on basically every command.
  • fnm appears to create multishell links named like /tmp/fnm_multishells/<pid>_<timestamp_ms>. In these sandboxes, the fnm process keeps getting the same tiny PID (3) because each command starts in a fresh PID namespace, while /tmp is shared across sandboxes.
  • That means two near-simultaneous commands can both try to create the exact same path.

I was able to reproduce the collision directly: I launched 6 parallel XDG_RUNTIME_DIR=/tmp fnm env --shell=bash calls under /bin/sh. Five succeeded with paths like 3_1774452861101 through 3_1774452861106, and one failed with:

Can't create the symlink for multishells at "/tmp/fnm_multishells/3_1774452861106". File exists

So the root cause is:

  1. fnm must be redirected away from /run/user/1000 in this sandbox.
  2. Bash is sourcing .bashrc during tool commands.
  3. fnm’s multishell naming collides across fresh PID namespaces sharing the same /tmp.

Your set -x; source .bashrc idea helped confirm that sourcing .bashrc itself is fine in isolation; the collision only shows up when multiple sandboxed command invocations hit fnm at nearly the same time.

This protects against collisions when multiple shells are started in parallel
inside Bubblewrap sandboxes, as each shell will have the same timestamp and the
same low PID (e.g. 2), since Bubblewrap sandboxes have isolated PID spaces.
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Mar 25, 2026

⚠️ No Changeset found

Latest commit: 3fe87e1

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@vercel
Copy link
Copy Markdown

vercel bot commented Mar 25, 2026

@joliss is attempting to deploy a commit to the Gal Schlezinger's projects Team on Vercel.

A member of the Team first needs to authorize it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant