diff --git a/tests/tools/test_process_registry.py b/tests/tools/test_process_registry.py index 9bda3a1656..d057a36c1e 100644 --- a/tests/tools/test_process_registry.py +++ b/tests/tools/test_process_registry.py @@ -361,6 +361,26 @@ def test_failed_reader_wait_does_not_publish_false_completion(registry, monkeypa assert moved == [] +def test_failed_reader_wait_still_records_known_exit_status(registry, monkeypatch): + """A PTY child reaped by isalive() has its status; a raising wait must not lose it.""" + session = _make_session(sid="proc_pty_wait_failed") + moved = [] + monkeypatch.setattr(registry, "_move_to_finished", lambda _s: moved.append(_s.id)) + + registry._finish_reader( + session, + MagicMock(decode=MagicMock(return_value="")), + lambda _text: None, + "PTY", + MagicMock(side_effect=OSError("waitpid ECHILD")), + lambda: 9, + ) + + assert session.exited is True + assert session.exit_code == 9 + assert moved == [session.id] + + # ========================================================================= # Incremental UTF-8 decoding across chunk boundaries # (ported from openclaw/openclaw#112325) diff --git a/tools/process_registry.py b/tools/process_registry.py index 3eb2c4d3f4..8330437b45 100644 --- a/tools/process_registry.py +++ b/tools/process_registry.py @@ -1178,8 +1178,12 @@ class ProcessRegistry(ProcessCheckpointMixin): try: wait() except Exception as e: - logger.warning("%s wait failed; leaving process tracked: %s", label, e) - return + # A PTY child reaped by isalive() already has its exitstatus; only an + # unknown status must stay tracked for later reconciliation. + if exit_code() is None: + logger.warning("%s wait failed; leaving process tracked: %s", label, e) + return + logger.warning("%s wait failed; recording known exit status: %s", label, e) self._finish_exited(session, exit_code()) @staticmethod