Skip to content

fix(cli): --worker reports whether the run actually happened - #292

Merged
kurok merged 1 commit into
mainfrom
fix/worker-exit-code
Sep 19, 2026
Merged

kurok merged 1 commit into
mainfrom
fix/worker-exit-code

Conversation

@kurok

@kurok kurok commented Sep 19, 2026 •

Copy link
Copy Markdown
Owner

Closes #240

The bug

run_worker_node returned None on every failure path — auth timeout, wrong message type, config timeout, HTTP/2 refusal, master closed — and the CLI did:

asyncio.run(run_worker_node(host, w_port, worker_secret=worker_secret))
sys.exit(0)

So systemd, a Kubernetes Job or a Jenkins agent could not tell a completed benchmark from a five-minute wait for a master that never appeared. Both were a success.

Two smaller edges came with it: ConnectionRefusedError escaped as a raw traceback, and Ctrl-C printed a KeyboardInterrupt traceback at whoever pressed it.

The fix

run_worker_node returns int: 0 only when a benchmark ran and its results reached the master; every give-up path returns 1. All eleven bare returns became return 1, and the contract is in the docstring.

The connect is wrapped, so an unreachable master is a logged error naming the address rather than a traceback. The CLI exits 130 on Ctrl-C — the shell's convention for SIGINT.

The return type changes from None to int, which is backwards-compatible for the existing callers that ignore it (pywrkr.run_worker_node is public API).

Tests

tests/test_distributed.py::TestWorkerExitCode, all four verified against the unpatched source:

  • test_worker_cli_exits_nonzero_when_master_sends_wrong_message — the issue's named case. Pre-fix AssertionError: None != 1.
  • test_worker_exits_nonzero_when_the_master_is_not_listening — pre-fix the test dies on ConnectionRefusedError: [Errno 61], which is the traceback the issue describes.
  • test_worker_exits_nonzero_when_the_master_hangs_up.
  • test_worker_exits_zero_after_a_completed_run — the success path still says success, and the master really did receive a result message. This one matters: returning 1 everywhere would also pass the first three.

The success test waits on an asyncio.Event set by the server handler rather than asserting straight after run_worker_node returns — the handler is its own task, and my first version raced it into an IndexError.

Checks

ruff format/check clean, mypy clean, 1917 passed.

run_worker_node returned None on every failure path -- auth timeout,
wrong message type, config timeout, HTTP/2 refusal, master closed -- and
the CLI did `asyncio.run(...); sys.exit(0)`. systemd, a Kubernetes Job or
a Jenkins agent could not tell a completed benchmark from a five-minute
wait for a master that never appeared. ConnectionRefusedError escaped as
a raw traceback, and Ctrl-C printed a KeyboardInterrupt traceback.

run_worker_node returns 0 only when a benchmark ran and its results
reached the master; every give-up path returns 1. The connect is wrapped
so an unreachable master is a logged error rather than a traceback, and
the CLI exits 130 on Ctrl-C.

Closes #240

Signed-off-by: Yurii Riabikov <22548029+kurok@users.noreply.github.com>

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@codecov

codecov Bot commented Sep 19, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 84.61538% with 4 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/pywrkr/distributed.py 90.47% 2 Missing ⚠️
src/pywrkr/main.py 60.00% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@kurok
kurok merged commit f2e5178 into main Sep 19, 2026
19 checks passed
@kurok
kurok deleted the fix/worker-exit-code branch September 19, 2026 12:48
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.

bug(cli): --worker always exits 0, even when the run never happened

1 participant