diff --git a/src/openjarvis/cli/agent_cmd.py b/src/openjarvis/cli/agent_cmd.py index 83ae51b6..15de4510 100644 --- a/src/openjarvis/cli/agent_cmd.py +++ b/src/openjarvis/cli/agent_cmd.py @@ -88,7 +88,13 @@ def list_agents() -> None: ) console.print(table) except Exception as exc: - console.print(f"[red]Error: {exc}[/red]") + # Escape the dynamic exception text: Rich re-parses console.print + # arguments as markup, so an exception message containing "[...]" + # raises a secondary MarkupError (see #297). Keep the static + # "Error:" label styled; render the message literally. + from rich.markup import escape + + console.print(f"[red]Error:[/red] {escape(str(exc))}") @agent.command("create") diff --git a/tests/cli/test_agent_cmd.py b/tests/cli/test_agent_cmd.py index 8a71c579..7dc2f9aa 100644 --- a/tests/cli/test_agent_cmd.py +++ b/tests/cli/test_agent_cmd.py @@ -2,6 +2,8 @@ from __future__ import annotations +from unittest.mock import patch + from click.testing import CliRunner from openjarvis.cli import cli @@ -12,6 +14,25 @@ class TestAgentCmd: result = CliRunner().invoke(cli, ["agents", "list", "--help"]) assert result.exit_code == 0 + def test_agent_list_error_with_markup_chars_does_not_crash(self) -> None: + """Regression for #297. + + When ``_get_manager()`` (or anything in the body) raises an + exception whose message contains Rich markup metacharacters like + ``[...]``, the error handler must not re-parse that text as markup + and blow up with a secondary MarkupError traceback. The command + should print a clean one-line error instead. + """ + # A message with bracketed text is what triggers the MarkupError + # when fed to console.print() unescaped. + boom = RuntimeError("DB locked at [/var/run/x] not a [valid] tag") + with patch("openjarvis.cli.agent_cmd._get_manager", side_effect=boom): + result = CliRunner().invoke(cli, ["agents", "list"]) + # No traceback / unhandled exception leaked through. + assert result.exception is None, result.output + # The raw message (brackets intact) is surfaced to the user. + assert "DB locked at [/var/run/x] not a [valid] tag" in result.output + def test_agent_info_help(self) -> None: result = CliRunner().invoke(cli, ["agents", "info", "--help"]) assert result.exit_code == 0