Skip to content

fix: emit JSON errors from agents/channels commands in --json mode - #1574

Open
Ramnath0521 wants to merge 1 commit into
TokenRhythm:mainfrom
Ramnath0521:fix/issue-1542
Open

fix: emit JSON errors from agents/channels commands in --json mode#1574
Ramnath0521 wants to merge 1 commit into
TokenRhythm:mainfrom
Ramnath0521:fix/issue-1542

Conversation

@Ramnath0521

Copy link
Copy Markdown

Refs #1542.

What is wrong

agents and channels commands accept --json but their failure paths call the human renderer directly, so a caller parsing the output gets a plain line instead of a document. sessions show --json, which the issue uses as a control, already does the right thing.

On main, Windows 11:

opensquilla agents delete qa-does-not-exist --force --json
# Error: 'Agent "qa-does-not-exist" not found'        exit 2

opensquilla channels describe qa-does-not-exist --json
# Error: "unknown channel type: 'qa-does-not-exist'"  exit 2

After the change, both emit one document on stderr and keep exit 2:

{"error": {"message": "'Agent \"qa-does-not-exist\" not found'", "code": "NOT_FOUND"}}
{"error": {"message": "\"unknown channel type: 'qa-does-not-exist'\"", "code": "NOT_FOUND"}}

The change

Both sites now go through the existing cli.output.emit_error. Its non-JSON branch is typer.secho(f"Error: {message}", fg=typer.colors.RED, err=True) — byte-identical to the calls it replaces — so human output does not change at all. channels certify already had this exact if json_output / else shape; this makes the rest of the file consistent with it rather than introducing a new pattern.

agents_cmd._fail() gained a json_output keyword and is now the single place that decides. Error codes come from the registry's own exception types rather than from guesswork:

  • _require_index raises KeyError for a missing agent -> NOT_FOUND
  • _normalize_user_agent_id (builtin rejection) and create_agent (duplicate id) raise ValueError -> INVALID_ARGUMENT

get_channel_setup_spec likewise raises KeyError for an unknown type -> NOT_FOUND.

Scope: why exactly three call sites

I audited every Error: ... emitter in both files rather than only the two the issue names. Nine sites exist; six are in commands that do not accept --json (channels add/remove/enable/disable/edit), so they are not part of this defect and are untouched. channels certify was already correct. That leaves three: agents delete and channels describe from the report, plus agents add, which has the identical defect through the same _fail helper and also advertises --json — fixing the helper without passing json_output at one of its two call sites would have been incoherent.

One thing deliberately left alone: the message text keeps Python's KeyError repr quoting ("'Agent \"x\" not found'"). That is what sessions show --json already produces for its own NOT_FOUND, and normalising it here would change the plain-text output too, which is out of scope for this issue.

Scope

Scope boundary: the --json failure paths of agents add, agents delete, and channels describe, routed through the existing emit_error helper.

Non-goals: the six channels subcommands that do not accept --json; channels certify, which was already correct; the message text/quoting produced by str(exc); and any success-path output.

Branch

Base branch: main

Target exception: N/A

Issue

Linked issue: Refs #1542

Release Note

Release note: opensquilla agents add|delete and opensquilla channels describe now emit a structured JSON error document in --json mode instead of a plain-text line.

Tests

Ruff: ruff check src tests — All checks passed.

Pytest: pytest tests/test_cli -q875 passed, 3 skipped. Full suite (-q -n auto --ignore=tests/functional --ignore=tests/live): 25,139 passed, 194 failed, 728 skipped.

Those 194 are pre-existing Windows-environment failures, not introduced here. They concentrate in test_opensquilla_home_migration.py (61) and test_turn_ingress_rpc.py (25), neither reachable from a CLI error-formatting change, and none are in agents_cmd/channels_cmd. Three did land in tests/test_cli (all in test_migrate_cmd.py); all three pass when run serially, as does the whole tests/test_cli directory. tests/functional must be run serially too — under -n auto its socket-binding gateway tests deadlock.

Build: mypy src/opensquilla --show-error-codes — Success: no issues found in 1547 source files.

Regression tests: added

Notes: Five cases across the two existing CLI test modules — three cover the bug, two are controls that pin the plain-text path so the fix cannot silently change human output:

Test Purpose
test_agents_delete_json_emits_structured_error_for_missing_agent the reported bug — fails on main
test_channels_describe_json_emits_structured_error_for_unknown_type the reported bug — fails on main
test_agents_add_json_emits_structured_error_for_duplicate same defect via the shared helper — fails on main
test_agents_delete_without_json_keeps_plain_text_error control — human output unchanged
test_channels_describe_without_json_keeps_plain_text_error control — human output unchanged

All three bug tests were written first and confirmed failing with json.decoder.JSONDecodeError: Expecting value before the implementation existed; both controls passed before and after. The pre-existing test_agents_add_duplicate_fails and test_agents_delete_main_rejected assert on combined stdout+stderr and still pass unchanged.

Maintainer Live Check

Maintainer live check: no

Surface: N/A

Third-Party Origin

Third-party origin: none


Authored by Claude (an AI coding agent) on the account owner's machine and with their authorization. The reproduction, the failing-test-first sequence and every check above were genuinely executed here rather than asserted; the account owner reviewed the diff before this was opened. Flagging the AI authorship plainly rather than leaving it to be inferred — happy to take any correction in review.

🤖 Generated with Claude Code

`opensquilla agents delete|add --json` and `opensquilla channels describe
--json` printed the human renderer's line on the failure path:

    Error: 'Agent "qa-does-not-exist" not found'

while `sessions show --json` already emits a structured document. A
caller parsing stdout/stderr as JSON cannot handle the failure path.

Route these through the existing cli.output.emit_error helper, whose
non-JSON branch is byte-identical to the typer.secho call it replaces,
so human output is unchanged. channels certify already had this
if/else and is the in-file precedent.

Error codes follow the registry's own exception types: _require_index
raises KeyError for a missing agent (NOT_FOUND), while a builtin-agent
rejection or duplicate id raises ValueError (INVALID_ARGUMENT).

Refs TokenRhythm#1542

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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