quaint-telephone-89068
03/13/2026, 7:32 PMpants run /some/python/code.py that itself does something like subprocess.check_output('pants run something/else.py. When the sandboxer is is enabled, this hangs (see below). We can work around it by setting env["PANTS_SANDBOXER"] = "false" which is fine for these corner cases.
#!/usr/bin/env bash
set -euo pipefail
cd $(mktemp -d)
cat > pants.toml <<EOF
[GLOBAL]
pants_version = "2.31.0"
backend_packages = [
"pants.backend.python",
]
[python]
interpreter_constraints = ["==3.11.*"]
EOF
mkdir hello caller
cat > hello/hello.py <<EOF
print("Hello, world!")
EOF
cat > hello/BUILD <<EOF
python_sources()
EOF
cat > caller/caller.py <<'EOF'
import os
import subprocess
import sys
env = {**os.environ, "PANTS_CONCURRENT": "true"}
result = subprocess.run(
["pants", "run", "hello/hello.py"],
capture_output=True,
text=True,
env=env,
)
print(result.stdout, end="")
print(result.stderr, end="", file=sys.stderr)
sys.exit(result.returncode)
EOF
cat > caller/BUILD <<EOF
python_sources()
EOF
echo "=== Step 1: pants run hello/hello.py (baseline) ==="
pants run hello/hello.py
echo
echo "=== Step 2: pants run caller/caller.py (subprocess calls pants run) ==="
pants run caller/caller.py
echo
echo "=== Step 3: same as step 2 but with sandboxer enabled ==="
echo " (expected: hangs)"
timeout 30 env PANTS_SANDBOXER=true pants run caller/caller.py || {
exit_code=$?
if [ $exit_code -eq 124 ]; then
echo "CONFIRMED: hung for 30s and was killed by timeout"
else
echo "Exited with code $exit_code"
fi
}
$ bash reproduce.sh
=== Step 1: pants run hello/hello.py (baseline) ===
15:23:51.80 [INFO] Initializing scheduler...
15:23:51.97 [INFO] Scheduler initialized.
15:23:54.37 [INFO] Completed: Scheduling: Find interpreter for constraints: CPython==3.11.*
15:23:54.39 [INFO] Completed: Scheduling: Building local_dists.pex
15:23:54.41 [INFO] Completed: Scheduling: Test binary /usr/bin/bash.
15:23:54.41 [INFO] Completed: Scheduling: Test binary /bin/bash.
15:23:54.43 [INFO] Completed: Scheduling: Building hello.pex
Hello, world!
=== Step 2: pants run caller/caller.py (subprocess calls pants run) ===
15:23:54.76 [INFO] Completed: Scheduling: Building caller.pex
Hello, world!
15:23:58.38 [INFO] Completed: Scheduling: Find interpreter for constraints: CPython==3.11.*
15:23:58.38 [INFO] Starting: Building local_dists.pex
15:23:59.63 [INFO] Completed: Building local_dists.pex
15:23:59.63 [INFO] Completed: Scheduling: Building local_dists.pex
15:23:59.64 [INFO] Completed: Scheduling: Test binary /bin/bash.
15:23:59.64 [INFO] Completed: Scheduling: Test binary /usr/bin/bash.
15:23:59.65 [INFO] Starting: Building hello.pex
15:24:00.88 [INFO] Completed: Building hello.pex
15:24:00.88 [INFO] Completed: Scheduling: Building hello.pex
=== Step 3: same as step 2 but with sandboxer enabled ===
(expected: hangs)
15:24:01.26 [INFO] Initialization options changed: sandboxer: False -> True. Reinitializing scheduler...
15:24:01.55 [INFO] Using sandboxer socket path /tmp/run/pants/874749acbb00656d/sandboxer.sock
15:24:01.63 [INFO] Scheduler initialized.
15:24:03.49 [INFO] Completed: Scheduling: Find interpreter for constraints: CPython==3.11.*
15:24:03.49 [INFO] Completed: Scheduling: Building local_dists.pex
15:24:03.51 [INFO] Completed: Scheduling: Test binary /usr/bin/bash.
15:24:03.52 [INFO] Completed: Scheduling: Test binary /bin/bash.
15:24:03.52 [INFO] Completed: Scheduling: Building caller.pex
CONFIRMED: hung for 30s and was killed by timeout
Traceback (most recent call last):
File "/tmp/pants-sandbox-Ascl58/./.cache/pex_root/venvs/3/68d48f7221c912ec582b9a8e3729214fdbb0f09c/73c7d7c618fac01449fa6e57a21210c0eb8a8f21/pex", line 380, in <module>
boot(
File "/tmp/pants-sandbox-Ascl58/./.cache/pex_root/venvs/3/68d48f7221c912ec582b9a8e3729214fdbb0f09c/73c7d7c618fac01449fa6e57a21210c0eb8a8f21/pex", line 355, in boot
runpy.run_module(module_name, run_name="__main__", alter_sys=True)
File "<frozen runpy>", line 226, in run_module
File "<frozen runpy>", line 98, in _run_module_code
File "<frozen runpy>", line 88, in _run_code
File "/tmp/pants-sandbox-Ascl58/./caller/caller.py", line 6, in <module>
result = subprocess.run(
^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/subprocess.py", line 550, in run
stdout, stderr = process.communicate(input, timeout=timeout)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/subprocess.py", line 1209, in communicate
stdout, stderr = self._communicate(input, endtime, timeout)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/subprocess.py", line 2115, in _communicate
ready = selector.select(timeout)
^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/selectors.py", line 415, in select
fd_event_list = self._selector.poll(timeout)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
KeyboardInterrupt
(An LLM wrote the reproduce.sh after git bisect pointed to this starting when we enabled the sandboxer)
pantsbuild/pants