Hi @danieldalto! I’m Dosu and I’m helping the Tuist team.
Your diagnosis is thorough and lines up with several real issues in the Command package. Here’s what the code and recent fixes reveal:
Fixes already shipped (and whether they cover your case)
The pipe-deadlock described in Command#226 motivated Command#249, which replaced the blocking waitUntilExit() with an async terminationHandler + CheckedContinuation. This shipped in tuist as Command 0.14.4 via PR #10682. It fixes the fan-out case (many concurrent swift package dump-package subprocesses starving the pool), but your case is different: a single swift-package … resolve subprocess producing large output. The stdoutTask and stderrTask inside CommandRunner.run are still unstructured Task {}s on the cooperative pool [1] — if swift-package is chatty enough (18 binary-artifact downloads logging progress), it can fill the pipe buffer faster than those reader tasks get scheduled on a 3-vCPU pool. That matches your observation exactly.
A follow-up fix — Command 0.14.9 / PR #11158 — added an AsyncResourceLimiter to bound concurrent subprocess launches by fd availability [2]. This prevents fd exhaustion but doesn’t change how the pipe-reader tasks are scheduled.
The most directly relevant fix for your symptom
PR #12069 — “bound and retry SwifterPM binary-artifact downloads” — merged July 25, adds per-request idle (60 s) and total transfer (600 s) timeouts plus retry logic to SwifterPM’s HTTP client. A stalled connection now surfaces as a bounded error rather than hanging open-ended. If your versions (4.182.0 / 4.204.0) predate this fix, upgrading is the first thing to try.
If #12069 is already in your version
Then your hang is more likely the pipe-buffer deadlock variant. Two things to confirm:
- Does the child
swift-packageproduce large stdout/stderr output? Redirecting to a file instead of a pipe would confirm the pipe-full hypothesis. - The
AsyncResourceLimiterderives its cap fromRLIMIT_NOFILE[3] — on a 3-vCPU VM with a soft limit of 256, the resulting cap is low, which could interact with how the pipe readers are scheduled.
The remaining gap is that the pipe-reader tasks are unstructured Task {}s competing on the cooperative pool. For a single subprocess with voluminous output on a very small pool, they can still be starved even after #249. Making these detached tasks or pinning them to a dedicated thread would close this case.
For a deeper discussion and to share those sample outputs and progress logs, I’d suggest posting on the community forum under Troubleshooting — that data will help the maintainers confirm whether this is pipe-buffer exhaustion or the SwifterPM timeout scenario from #12069.
To reply, just mention @dosu.
Docs are dead. Just use Dosu.