Skip to content

fix(secrets): keep the stop reason when macOS reports EPERM on kill - #49

Merged
whitelonng merged 1 commit into
mainfrom
fix/macos-secrets-kill
Oct 25, 2026
Merged

whitelonng merged 1 commit into
mainfrom
fix/macos-secrets-kill

Conversation

@whitelonng

Copy link
Copy Markdown
Owner

Summary

On macOS, a secret action that hit its output limit returned EXECUTOR_FAILED instead of OUTPUT_LIMIT.

The sequence:

  1. stop() runs once per stdout chunk, so it can be called more than once.
  2. The second call signals the process group after the leader has exited but before it has been reaped.
  3. In that state macOS returns EPERM, where Linux returns ESRCH.
  4. kill() treats any error other than ESRCH as EXECUTOR_FAILED, and it overwrote the code already set.

The kill('SIGKILL') in finish() could hit the same race, so a successful run could also be reported as failed.

Fix (src/secrets/run.ts):

  • Signal the group only once per stop request.
  • Treat EPERM as "already gone" once a stop was requested or the child closed.
  • Never overwrite an earlier failure code (??=).

Test plan

  • New deterministic regression test: later group signals throw EPERM, and the receipt must keep OUTPUT_LIMIT. It fails without the fix (EXECUTOR_FAILED) and passes with it.
  • The previously failing "handles output without retries or raw exceptions" test passed 6 out of 6 runs. Without the fix it failed 3 out of 3.
  • npm run check passes locally on macOS: 189 files, 1760 tests.

The executor lifecycle tests are skipped on non-macOS, so CI on ubuntu does not exercise this path.

When output exceeded the limit, `stop('OUTPUT_LIMIT')` ran once per stdout
chunk. The second call signalled the process group after its leader had
exited but before it was reaped; macOS returns EPERM there (Linux returns
ESRCH), and `kill` overwrote the failure with EXECUTOR_FAILED. Receipts on
macOS therefore reported the wrong code, and the finish-time SIGKILL could
do the same on a successful run.

- Signal the group only once per stop request.
- Treat EPERM as "already gone" once a stop was requested or the child
  closed.
- Never overwrite an earlier failure code (`??=`).

Add a deterministic regression test that makes later group signals throw
EPERM. The executor lifecycle tests only run on macOS, so CI (ubuntu)
does not exercise this path.
@whitelonng
whitelonng merged commit 2159b79 into main Oct 25, 2026
6 checks passed
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