Skip to content

Report an unreadable input, rather than tracebacking out of the CLI - #73

Merged
Shashankss1205 merged 1 commit into
mainfrom
fix/cli-unreadable-inputs
Aug 3, 2026
Merged

Report an unreadable input, rather than tracebacking out of the CLI#73
Shashankss1205 merged 1 commit into
mainfrom
fix/cli-unreadable-inputs

Conversation

@Shashankss1205

Copy link
Copy Markdown
Collaborator

Fixes #63. Fixes #62.

#63 — four reads that could raise a type nobody caught

command before after
trace/metrics/viz <directory> --json exit 1, empty stdout, IsADirectoryError traceback exit 2, {"ok": false, "error": "unreadable trace file: adir: Is a directory"}
trace <unreadable file> exit 1, PermissionError traceback exit 2, one document
run <non-UTF-8 file> --json exit 1, UnicodeDecodeError traceback exit 2, one document naming the codec
demo/run/plan with a binary grapharc.toml exit 1, UnicodeDecodeError traceback exit 2, ConfigError naming the file

Three root causes, all the same shape — a read whose failure mode the surrounding handler does not cover:

  • _existing_trace (cli/main.py) tested path.exists(), and the three reader handlers catch only TraceReadError, so every other OSError went past both. It now opens the file where the failure is still reportable.
  • cli/graphrun.py and cli/config.py each had read_text(encoding="utf-8") outside the except that catches their decoder. UnicodeDecodeError is a ValueError, so neither JSONDecodeError nor TOMLDecodeError was ever going to catch it.

replay/diff already handled this correctly, which is what marked the rest as an oversight rather than a decision. The config case matters most: grapharc.toml is picked up implicitly from the working directory, so a stray binary file there broke every configurable command.

#62plan --approve --json emitted unparseable output

The park notice printed to stdout ahead of the document, so json.load(stdout) raised. It is now silent in JSON mode (matching cli/live.py, which already guards its announce) and unchanged in text mode — a human still gets waiting for approval (up to 3s) — answer with: grapharc approve <dir>.

Tests

Six new tests, each confirmed red against the stashed pre-fix source:

  • test_a_directory_where_a_trace_belongs_{is_a_report_not_a_traceback,fails_as_one_json_document}, parametrized over trace/metrics/viz
  • test_run_reports_a_graph_file_that_is_not_utf8
  • test_a_config_that_is_not_utf8_names_the_file
  • test_plan_approve_in_json_mode_emits_one_document — asserts json.loads(stdout) succeeds and stderr is empty
  • test_plan_approve_in_text_mode_still_announces_how_to_answer — a guard so silencing JSON mode cannot silence humans

Verification

pytest1785 passed, 12 deselected. ruff check grapharc tests → clean. No flaky timing failures in this run.

🤖 Generated with Claude Code

Four reads could raise a type their caller did not catch, so a directory,
an unreadable file or a non-UTF-8 one exited 1 with a traceback where the
contract promises exit 2 and a document: _existing_trace tested exists()
while the handlers catch only TraceReadError, and two read_text calls sat
outside their decoders' except (UnicodeDecodeError is a ValueError, so
neither would ever have caught it). The config case is the worst of them,
because grapharc.toml is picked up implicitly from the working directory.

plan --approve also printed its park notice ahead of the JSON document,
which made the whole output unparseable on the flag most likely to be
driven unattended; the notice is now silent in JSON mode and unchanged
for a human.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Shashankss1205
Shashankss1205 merged commit fc1503c into main Aug 3, 2026
6 checks passed
@Shashankss1205
Shashankss1205 deleted the fix/cli-unreadable-inputs branch August 3, 2026 19:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant