Skip to content

An invalid decision record crashes decisions show/approve/list with exit 1 instead of a typed error #41

Closed

Description

@shipmill-romamo

What happens

DecisionStore.load (sdk/src/cloudfall/decision.py) validates a stored decision record with catalog.validate_named(DECISION_SCHEMA, document) and lets the schema error escape. A record on disk that fails the schema makes cloudfall decisions show <id>, decisions approve <id>, decisions list and why exit 1 instead of answering with a typed error envelope. One example is a hand-edited approval.via with a value that isn't terminal or unknown.

Found while reviewing #37. It is already true on main for any invalid record, not only the new via field.

Reproduce

$ cloudfall operations propose shop-logrotate --target hz1
# edit decisions/<id>.json: set "status": "bogus"
$ cloudfall decisions show <id>
# exit 1, no envelope

Expected

A record that fails the schema answers with a typed error (for example RECORD_INVALID) that names the file and the failing path, with its own exit code. decisions list and why must not hide one bad record behind a crash.

Activity

  1. shipmill-romamo commented on Oct 8, 2026

    @shipmill-romamo
    ContributorAuthor

    Triage: implement

    Confirmed on main (964d889):

    • DecisionStore.load (sdk/src/cloudfall/decision.py) calls catalog.validate_named(DECISION_SCHEMA, document) and lets the schema library's ValidationError escape. The commands only catch DecisionError, so the error never reaches their RECORD_INVALID mapping and the CLI exits 1 with no envelope
    • The same happens one step earlier for a file that isn't JSON: json.loads raises JSONDecodeError, also uncaught
    • decisions list loads every record through load, so one bad file breaks the whole list, and why and decisions approve go through the same path

    Plan. No new exit code is needed: decisions list, decisions show, decisions approve and why already declare RECORD_INVALID and map DecisionError to it (_record_invalid in sdk/src/cloudfall/app.py). load will turn a malformed or schema-invalid record into a DecisionError with a new code decision_record_invalid, whose message names the file and the failing JSON path. decisions list and why stay fail-fast: they answer RECORD_INVALID naming the bad file instead of skipping it, so a broken record is never hidden. Regression tests cover a non-JSON file, a bad status, and a bad approval.via, for show, list, approve and why.

  2. shipmill-romamo commented on Oct 8, 2026

    @shipmill-romamo
    ContributorAuthor

    PR: #43. DecisionStore.load now turns a record that isn't JSON (or isn't UTF-8) or fails the schema into RECORD_INVALID (exit 86, code decision_record_invalid), naming the file and the failing field. decisions show, list, approve and why all answer with it; approve refuses before asking anyone to type the id. list and why still stop at the first bad record instead of skipping it, as planned. CI is green; it goes to review and merge next.

  3. shipmill-romamo commented on Oct 9, 2026

    @shipmill-romamo
    ContributorAuthor

    Released in v0.6.0rc7.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions