Two distinct flake sources hit four separate runs today. Neither is a code defect, and both have narrow fixes. Filing with evidence so the next red is triaged in seconds rather than by bisecting a diff.
1. pnpm/action-setup races on a shared install dir
Error: ENOTEMPTY: directory not empty, rmdir
'/Users/taniguchiryusei/setup-pnpm/node_modules/.bin/store/v11/files/03'
The action installs into ~/setup-pnpm, which is shared across every job on the self-hosted runner. Two jobs from different refs starting close together race on it, and the loser fails before a single test runs.
The workflow's concurrency group is keyed on github.ref, so it correctly serialises runs for one branch but does nothing across branches — which is exactly the case that collides.
Seen on: run 33297952151 (PR #124). A re-run with no code change passed.
Fix options, cheapest first:
standalone: true on pnpm/action-setup — installs a self-contained binary, skipping the node_modules layout that races.
- or
dest: ~/setup-pnpm-${{ github.run_id }} — unique per run, at the cost of leaving directories behind.
2. A post-teardown 57P01 fails a run where every test passed
Run 33297710143 (main @ 97a3c23):
Test Files 120 passed (120)
Tests 865 passed (865)
Errors 1 error
→ exit code 1
The error is a Postgres FATAL 57P01 ("terminating connection due to administrator command") surfacing after returns zeros on Plaid API error (non-fatal) completed, in tests/integration/recurring-sync.test.ts. A pooled connection is still open when the testcontainer is torn down, so the reset lands as an unhandled error outside any test and Vitest exits non-zero.
Note this is not the hook-timeout problem — that one is fixed (hookTimeout: 30_000, PR #122) and no hook timed out in this run. Every assertion passed. Only the teardown ordering is wrong.
Likely fix: ensure the pool is drained before the container stops — await pool.end() in the afterEach/global teardown path rather than letting the container shutdown race an open client.
Why it is worth fixing rather than re-running
Both produce a red X that looks like a broken diff. Today that cost a full triage cycle on main and on two PRs, and the natural reaction — reverting the most recent change — is exactly wrong. See #103 for the related mutation-gate noise, and the pattern already recorded there of a revert being opened for a red the commit did not cause.
Two distinct flake sources hit four separate runs today. Neither is a code defect, and both have narrow fixes. Filing with evidence so the next red is triaged in seconds rather than by bisecting a diff.
1.
pnpm/action-setupraces on a shared install dirThe action installs into
~/setup-pnpm, which is shared across every job on the self-hosted runner. Two jobs from different refs starting close together race on it, and the loser fails before a single test runs.The workflow's
concurrencygroup is keyed ongithub.ref, so it correctly serialises runs for one branch but does nothing across branches — which is exactly the case that collides.Seen on: run 33297952151 (PR #124). A re-run with no code change passed.
Fix options, cheapest first:
standalone: trueonpnpm/action-setup— installs a self-contained binary, skipping thenode_moduleslayout that races.dest: ~/setup-pnpm-${{ github.run_id }}— unique per run, at the cost of leaving directories behind.2. A post-teardown
57P01fails a run where every test passedRun 33297710143 (main @
97a3c23):The error is a Postgres
FATAL 57P01("terminating connection due to administrator command") surfacing afterreturns zeros on Plaid API error (non-fatal)completed, intests/integration/recurring-sync.test.ts. A pooled connection is still open when the testcontainer is torn down, so the reset lands as an unhandled error outside any test and Vitest exits non-zero.Note this is not the hook-timeout problem — that one is fixed (
hookTimeout: 30_000, PR #122) and no hook timed out in this run. Every assertion passed. Only the teardown ordering is wrong.Likely fix: ensure the pool is drained before the container stops — await
pool.end()in theafterEach/global teardown path rather than letting the container shutdown race an open client.Why it is worth fixing rather than re-running
Both produce a red X that looks like a broken diff. Today that cost a full triage cycle on
mainand on two PRs, and the natural reaction — reverting the most recent change — is exactly wrong. See #103 for the related mutation-gate noise, and the pattern already recorded there of a revert being opened for a red the commit did not cause.