st2 up --once exits 0 when the reconcile pass was skipped, so exit status is not evidence that a reconcile happened.
Verified at 4e25f56. Apologies if this is already addressed in work you have locally.
Mechanism
When pty list --json fails, the pass returns early with the error recorded in the report:
src/run.rs:645 and src/run.rs:803 — report.errors.push(format!("list sessions (pass skipped): {e}")); return report;
--once prints the report and returns Ok(()) regardless:
src/main.rs:1691-1696 (catalog path) and src/main.rs:1588-1595 (st2 up <spec> --once)
So "no pass happened" and "a pass happened and everything is fine" are the same exit status. print_report writes the errors to stderr, but stderr is not what a supervisor, systemd ExecStartPre, or a CI step checks.
Reproduction
At 4e25f56, with a failing pty placed first on PATH for that one invocation only:
cat > "$SHIM/pty" <<'SH'
#!/bin/sh
echo "pty: simulated failure" >&2
exit 1
SH
chmod +x "$SHIM/pty"
( PATH="$SHIM:$PATH" st2 up --catalog "$CATALOG" --host <host> --once; echo "EXIT=$?" )
reconcile pass on host '<host>':
error: list sessions (pass skipped): `pty list --json` failed: pty: simulated failure
EXIT=0
Nothing was launched. Exit 0.
Impact for an adopter
st2 up --once is the natural building block for a one-shot boot step — a deploy hook, a unit ExecStart, a health gate. We use it exactly that way. A transient pty failure makes it a silent no-op that reports success, and the fleet stays down while the orchestration layer records a clean run. The distinction that matters is not "were there errors" (an individual agent can legitimately fail while the pass still ran) but "did a reconcile happen at all".
Offer
We have a PR with a regression test: an explicit skipped flag on UpReport, set at the two early returns, with --once exiting non-zero when it is set. That keeps "a pass ran and had per-agent errors" distinct from "no pass ran", and leaves the supervising loop's behaviour untouched. Happy for you to reshape or reject it — the same flag would also let the loop escalate a persistently skipping pass, but we did not presume to change loop semantics.
st2 up --onceexits0when the reconcile pass was skipped, so exit status is not evidence that a reconcile happened.Verified at
4e25f56. Apologies if this is already addressed in work you have locally.Mechanism
When
pty list --jsonfails, the pass returns early with the error recorded in the report:src/run.rs:645andsrc/run.rs:803—report.errors.push(format!("list sessions (pass skipped): {e}")); return report;--onceprints the report and returnsOk(())regardless:src/main.rs:1691-1696(catalog path) andsrc/main.rs:1588-1595(st2 up <spec> --once)So "no pass happened" and "a pass happened and everything is fine" are the same exit status.
print_reportwrites the errors to stderr, but stderr is not what a supervisor,systemdExecStartPre, or a CI step checks.Reproduction
At
4e25f56, with a failingptyplaced first onPATHfor that one invocation only:Nothing was launched. Exit
0.Impact for an adopter
st2 up --onceis the natural building block for a one-shot boot step — a deploy hook, a unitExecStart, a health gate. We use it exactly that way. A transientptyfailure makes it a silent no-op that reports success, and the fleet stays down while the orchestration layer records a clean run. The distinction that matters is not "were there errors" (an individual agent can legitimately fail while the pass still ran) but "did a reconcile happen at all".Offer
We have a PR with a regression test: an explicit
skippedflag onUpReport, set at the two early returns, with--onceexiting non-zero when it is set. That keeps "a pass ran and had per-agent errors" distinct from "no pass ran", and leaves the supervising loop's behaviour untouched. Happy for you to reshape or reject it — the same flag would also let the loop escalate a persistently skipping pass, but we did not presume to change loop semantics.