fix(cli): handle click.Abort to show 'Aborted.' instead of empty unexpected error (#13062) - #13063
Conversation
There was a problem hiding this comment.
1 issue found across 1 file
Confidence score: 4/5
sdks/python-cli/omi_cli/main.pychangesclick.Aborthandling from an empty unexpected-error/exit 1 response toAborted./exit 130, so CLI behavior could regress without coverage; add atests/test_main.pyregression test that exercisesmain()withapp()raisingclick.Abort.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="sdks/python-cli/omi_cli/main.py">
<violation number="1" location="sdks/python-cli/omi_cli/main.py:238">
P2: This fix changes observable behavior (empty "unexpected error"/exit 1 → "Aborted."/exit 130) but adds no regression test in tests/test_main.py. Add a test that calls main() with app() raising click.Abort (via monkeypatch) and asserts exit code 130 and "Aborted." on stderr, so the fix isn't silently lost.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| except typer.Exit as exc: | ||
| sys.exit(exc.exit_code) | ||
| except (KeyboardInterrupt, EOFError): | ||
| except (KeyboardInterrupt, EOFError, click.Abort): |
There was a problem hiding this comment.
P2: This fix changes observable behavior (empty "unexpected error"/exit 1 → "Aborted."/exit 130) but adds no regression test in tests/test_main.py. Add a test that calls main() with app() raising click.Abort (via monkeypatch) and asserts exit code 130 and "Aborted." on stderr, so the fix isn't silently lost.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At sdks/python-cli/omi_cli/main.py, line 238:
<comment>This fix changes observable behavior (empty "unexpected error"/exit 1 → "Aborted."/exit 130) but adds no regression test in tests/test_main.py. Add a test that calls main() with app() raising click.Abort (via monkeypatch) and asserts exit code 130 and "Aborted." on stderr, so the fix isn't silently lost.</comment>
<file context>
@@ -235,7 +235,7 @@ def main() -> None:
except typer.Exit as exc:
sys.exit(exc.exit_code)
- except (KeyboardInterrupt, EOFError):
+ except (KeyboardInterrupt, EOFError, click.Abort):
sys.stderr.write("\nAborted.\n")
sys.exit(130)
</file context>
|
Thanks @armorbreak001 — nice diagnosis and a clean first contribution. I verified the root cause: Two small follow-ups, neither blocking:
The fix itself is correct as-is and resolves the reported behavior. Leaving the merge call to a human maintainer. This review was produced by the repository's automated review assistant on behalf of the maintainers. by AI on behalf of David — if you need David’s attention urgently, please @Git-on-my-level and escalate with |
|
Thanks for picking up #13062. I had already prepared the same handler fix locally while investigating the report; I will not open a competing implementation PR. For the requested regression coverage, here is our tested patch. It exercises the real main() entry point, login command and Click prompt with controlled stdin, rather than replacing app() with an exception. Both Ctrl-C and EOF cases fail with the old handler (exit 1 instead of 130) and pass with click.Abort handled. Targeted suite: 8 passed; full CLI suite: 130 passed, 1 Windows-only skip on Linux ARM64/Python 3.14.5. We also reproduced SIGINT at the prompt in a PTY subprocess (exit 1 before, 130 after). No live authentication or credentials used. diff --git a/sdks/python-cli/tests/test_main.py b/sdks/python-cli/tests/test_main.py
index edc2275..285c2e5 100644
--- a/sdks/python-cli/tests/test_main.py
+++ b/sdks/python-cli/tests/test_main.py
@@ -2,12 +2,36 @@
from __future__ import annotations
+import io
import json
+import pytest
+
from omi_cli import __version__
from omi_cli.main import app
+@pytest.mark.parametrize("interruption", [KeyboardInterrupt, EOFError])
+def test_login_prompt_interruption_exits_cleanly(config_path, monkeypatch, capsys, interruption) -> None:
+ from omi_cli.main import main
+
+ class InterruptedInput(io.StringIO):
+ def isatty(self):
+ return True
+
+ def readline(self, *args, **kwargs):
+ raise interruption
+
+ monkeypatch.setattr("sys.stdin", InterruptedInput())
+ monkeypatch.setattr("sys.argv", ["omi", "auth", "login"])
+ with pytest.raises(SystemExit) as exc:
+ main()
+ assert exc.value.code == 130
+ stderr = capsys.readouterr().err
+ assert "Aborted." in stderr
+ assert "unexpected error" not in stderr
+
+
def test_version_flag(cli_runner) -> None:
result = cli_runner.invoke(app, ["--version"])
assert result.exit_code == 0Feel free to incorporate this coverage into this PR. This investigation and test contribution are AI-assisted by Hermes on behalf of @LNLenost. The optional bounty request on #13062 remains unapproved; this comment does not claim an award or payment. |
|
Thanks @LNLenost! Really appreciate you sharing the regression test patch — I'll integrate it into this PR shortly. The handler fix was straightforward but having proper test coverage through the real main() entry point is exactly what's needed for a complete fix. I'll review your suggested approach and make sure it's included before the next update. 🙏 |
…Abort Add parametrized tests exercising the real main() entry point with KeyboardInterrupt and EOFError raised at an interactive login prompt. Verifies 'Aborted.' on stderr, exit code 130, and no 'unexpected error' leak. Patch contributed by @LNLenost; handler rung for click.Abort included (RuntimeError subclass falls past KeyboardInterrupt handler).
266d761 to
0da9310
Compare
|
Integrated — thanks again @LNLenost! 🙏 What's in the update:
Verification:
Ready for re-review whenever you have a moment. |
kodjima33
left a comment
There was a problem hiding this comment.
Bug fix: click.Abort not caught by KeyboardInterrupt/EOFError handler on some Click versions, prints empty unexpected-error instead of 'Aborted.'; confirmed still present on main. One-line fix, linked #13062, tests added. Confidence 4/5.
Summary
Fixes #13062
Bug: Interrupting an interactive prompt (e.g.
omi auth loginmethod selection) with Ctrl-C printsomi: unexpected error: `` (empty message) and exits 1, instead of the intendedAborted.` with exit code 130.Root cause: Click raises
click.Aborton prompt interruption. In some Click versions,click.Abortdoes not inherit fromclick.ClickException, so it falls through the exception ladder past theClickExceptionhandler and hits the genericExceptionhandler — which formats it as an empty "unexpected error".Change
One-line fix in
sdks/python-cli/omi_cli/main.py: addclick.Abortto the existingexcept (KeyboardInterrupt, EOFError)clause.This matches the documented intent in the comment above the handler: "KeyboardInterrupt / EOFError — Ctrl-C / Ctrl-D. Conventional 130."
Testing
omi auth login, pressed Ctrl-C at the method-selection prompt → printsAborted.and exits 130ast.parse)tests/test_main.py(if present) should continue to pass since this only adds an exception type to an existing handler