Skip to content

The batch commands disagree about exit status, and upload --json emits two documents #27

Closed

Description

@ergofobe

Symptom

The three batch commands disagree with each other about exit status, and one of them emits a
--json stream that cannot be parsed. Three related defects:

1. assets edit exits 0 when every edit was rejected. It prints Edited 0 photographs. and
returns Ok(()). upload and download both bail! in the equivalent case. A script that
submits 100 edits and has all 100 refused by the server sees success.

This one directly undercuts #23: that issue made the rejection say which field was wrong, but
a caller checking $? still never learns anything was wrong at all.

2. upload --json emits two JSON documents when a file fails. ctx.out.json(...)? writes the
result, then bail! sends main::report to write {"error":"N files failed",…} to the same
stdout. The stream is then two concatenated documents, which breaks jq and any ordinary parser.
src/output.rs states the contract this contradicts.

3. download --json exits 0 while the identical run in prose exits 1. Its JSON branch returns
before the bail!. The mirror image of (1) — and it means the same failure reports differently
depending on which audience asked.

Reproduction

  1. imogen assets edit <id> <id> --taken-at <value the server refuses>; check $? — 0.
  2. imogen upload <dir> --json with one file the server rejects; pipe to jq — parse error on
    trailing content.
  3. imogen download <selection> --json where a file fails; check $? — 0. Run the same without
    --json; $? is 1.

Expected

One rule, applied by all three: a batch in which every item failed exits non-zero; a partial
failure exits with whatever the commands agree on (and they should agree); and --json emits
exactly one document on stdout whatever happens, with the failure represented inside it
rather than appended after it.

Worth settling the partial-failure case deliberately rather than per-command, since that is how
the three drifted apart.

Affected repos

Only this one. Nothing on the wire changes.

Context

Found by review while implementing #23 and #24, both of which were about the content of a
failure report. This is about its exit status and framing, which is the half a script actually
depends on. Pre-existing in all three commands.

Activity

  1. added
    bugSomething isn't working
    triagedHome repo decided, labelled, ready to bundle
    on Sep 12, 2026
  2. ergofobe commented on Sep 16, 2026

    @ergofobe
    OwnerAuthor

    Fixed in #39, merged as 455d52c.

    The rule the three now share, from finish_batch in src/commands/mod.rs:

    • 0 when nothing was refused, 1 when nothing got through, 3 when some items succeeded and some were refused. 2 is left to clap's usage error, so a script can tell "you called me wrong" from "some of it failed". The partial case earns its own code because "the server is down, nothing landed" and "ninety-seven landed, three were refused" call for different things — stopping and shouting versus reading failures and retrying those three.
    • --json puts exactly one document on stdout. A failing batch writes nothing there itself: the summary travels out with BatchFailure and main::report writes it once, with the sentence added under error.
    • A run that got nothing through no longer closes with a green line claiming it did.

    Behaviour change for scripts, noted in the PR body: an all-refused edit and a failing download --json now exit non-zero where they exited 0, and a partial upload/download exits 3 where it exited 1.

    Tests are in tests/batch_exit_status.rs, run against a stub server so $? and the stdout bytes are the real ones; the single-document property is asserted by parsing the whole stream and counting.

    The album-fill failure a few lines above the new finish_batch call is still ctx.out.warn(...) and still exits 0 — that is #34's second half, left where it is.

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 workingtriagedHome repo decided, labelled, ready to bundle

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions