fix: actionable errors for malformed --data instead of a raw traceback - #32
Merged
Conversation
post, patch, and action each had their own unguarded json.loads() on --data/-d, so a shell-mangled inline literal (PowerShell strips quotes routinely) or a bare file path passed without the @ prefix surfaced a raw json.JSONDecodeError traceback instead of telling the user what to fix. Extract the parsing into bcli_cli._data_arg.parse_data_argument and raise typer.BadParameter on every failure, with the JSON error's line/column/reason, a short excerpt of the input, and a one-line hint when the input looks like a filesystem path or a shell-mangled literal.
igor-ctrl
force-pushed
the
fix/data-json-errors
branch
from
August 11, 2026 21:22
f6af0b6 to
39b283c
Compare
The path heuristic called Path(data).is_file() on the raw argument. On Linux any argument over 255 bytes raises OSError(ENAMETOOLONG) and an embedded NUL raises ValueError, so a long malformed payload — the kind most likely to be malformed in the first place — escaped as a raw traceback: precisely the failure this module was added to remove. macOS does not raise, so it passed locally and only surfaced on CI. Both filesystem probes are now guarded: the heuristic treats an unusable name as "not a path", and the @file branch reports "File not found" rather than propagating OSError. A file that exists but can't be read now reports "Could not read <path>: <reason>" instead of an OSError traceback. Regression tests cover long, long-and-JSON-ish, long-path-shaped, NUL-bearing payloads and a long @path. They pass trivially on macOS and are meaningful on Linux/CI, which is where the defect lives.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
post,patch, andactioneach carried their own copy of an unguardedjson.loads()for--data/-d. A shell-mangled inline literal (PowerShell strips quotes from{"a": 1}routinely) or a bare file path passed without the@prefix surfaced a ~25-line rawjson.JSONDecodeErrortraceback instead of telling the user what to fix.bcli_cli._data_arg.parse_data_argument()(the codebase already had a pattern of single-purpose_*.pyhelper modules shared across commands —_safety.py,_url_resolve.py,_envelope_wrap.py— so this follows suit rather than leaving three drifting copies).typer.BadParameter(never a bareJSONDecodeError): the message includes the JSON error's line/column/reason and a short excerpt of what was received (truncated to ~80 chars, never the whole payload), plus a one-line hint when the input looks like a filesystem path (missing@) or a shell-mangled literal (starts with{but has no quotes, or has unquoted keys).@filenow names the file in its error. A missing@filekeeps the existing "File not found" message unchanged.Changes
src/bcli_cli/_data_arg.py— new shared helper,parse_data_argument().src/bcli_cli/commands/{post,patch,action}_cmd.py— use the shared helper, removed the three duplicated_parse_data()definitions.tests/test_cli/test_data_arg.py— new, full heuristic matrix for the shared helper.tests/test_cli/test_post_cmd.py,tests/test_cli/test_patch_cmd.py— new, command-level coverage (these two commands had no dedicated test file before).tests/test_cli/test_action_cmd.py— extendedTestDataHandlingwith the same error-path cases.CHANGELOG.md—[0.8.1]entry.pyproject.toml/uv.lock— version bump to0.8.1(src/bcli/_version.pyreads from package metadata at runtime, so it needed no change).docs/command-reference.md,docs/write-operations.md— documented that@is required (a bare path is parsed as JSON, not silently treated as a file) and the PowerShell quote-stripping gotcha.Test plan
uv run --extra dev python -m pytest tests/ -q— 1131 passed, 5 skipped (pre-existing, unrelated), 3 warnings (pre-existing, unrelated to this change).uv run ruff check src/ tests/— all checks passed.parse_data_argument()directly (valid inline, valid@file, shell-mangled with/without quotes, bare existing file path, Windows drive path, path-like-but-missing, missing@file, malformed@file, generic garbage) — every failure path raisestyper.BadParameterwith the expected hint, none leak a rawJSONDecodeError.