Skip to main content
← Back to list
01Issue
BugClosedSwamp CLIPublicTeam
AssigneesNone

Relationships

#2536 command/shell: a non-zero exit drops stdout/stderr from the --json result, the result and log data artifacts, and the method-summary report

Opened by matthewbonig · 9/25/2026

Summary

When a command/shell execute exits non-zero, the command's captured stdout and stderr become unreachable to any consumer that reads the run's structured result. The --json failure payload carries only the error string, neither the result resource nor the log file data artifact is written, and @swamp/method-summary records only the exit code.

The output is not lost from disk — it is written to .swamp/outputs/command/shell/execute/<modelId>-<timestamp>.log — but nothing in the failure path points at that file. On success the --json payload includes a logFile field; on failure it does not.

The practical effect is that a failing shell step is undiagnosable from its own output. In this repo that cost five consecutive pre-pr-verify runs, each of which reported only its exit code. The output was recovered only by re-running the identical command with ignoreExitCode=true.

Reproduction

Identical command, run twice against the same command/shell model, differing only in ignoreExitCode.

Failing (default):

swamp model method run exman-shell execute \
  --input run='echo "MARKER-STDOUT-LINE"; echo "MARKER-STDERR-LINE" >&2; exit 3' \
  --json
{
  "error": "Command exited with code 3",
  "code": "method_execution_failed"
}

That is the entire payload. No stdout, no stderr, no exitCode, no logFile, no dataArtifacts.

Same command with --input ignoreExitCode=true:

{
  "status": "succeeded",
  "logFile": ".../.swamp/outputs/command/shell/execute/5bd6f0fc-...-2026-09-25T16-09-22-626Z.log",
  "dataArtifacts": [
    {
      "name": "result",
      "attributes": {
        "exitCode": 3,
        "command": "echo \"MARKER-STDOUT-LINE\"; echo \"MARKER-STDERR-LINE\" >&2; exit 3",
        "durationMs": 5,
        "stdout": "MARKER-STDOUT-LINE",
        "stderr": "MARKER-STDERR-LINE"
      }
    },
    { "name": "log" }
  ]
}

The process ran identically in both cases — same exitCode: 3, same output. Only the reporting differs.

Three separate consumers lose the output

1. The --json payload. Shown above. This is the path agents and CI use.

In human (non---json) mode the output does stream to the terminal:

  Executing   execute · 16:12:03 UTC
              MARKER-STDOUT-LINE
              MARKER-STDERR-LINE

Error: Command exited with code 3

So this is specifically a structured-output defect, not a capture defect.

2. The result and log data artifacts are never written. After the failing run, swamp data list exman-shell shows only the report artifacts. After the ignoreExitCode run it shows result v1 and log v1. A second failing run afterwards left both at v1 — the failure path writes neither.

Both are declared on the type (swamp model type describe command/shell): result carries stdout/stderr/exitCode, and log is a streaming: true text/plain file resource. Because neither is written, swamp data query and data.latest(<name>, 'result') cannot reach a failed step's output after the fact. Post-hoc diagnosis of a past failure is not possible at all — only re-running is.

3. @swamp/method-summary records only the exit code. This is the documented place to look after a failure — the swamp skill's troubleshooting guidance and this repo's conventions both say reports run even on failure and capture structured diagnostics. Here it captures nothing usable:

{
  "status": "failed",
  "error": "Command exited with code 3",
  "narrative": "execute on exman-shell (command/shell) failed: Command exited with code 3",
  "methodArgs": ["run"]
}

The markdown rendering has an ## Error section containing the same one line. The report enumerates the result spec under outputSpecs, including its stdout and stderr properties, while carrying no value for either.

Expected behaviour

A non-zero exit is an ordinary, expected outcome for a shell command — it is the outcome you most need the output for. At minimum the failure should be diagnosable without re-running:

  • the --json failure payload should carry exitCode, stdout, stderr and the logFile path, or at least the logFile path;
  • the result and log artifacts should be written before the method throws, so the output survives in queryable data;
  • @swamp/method-summary should include the captured output for a failed shell method, since diagnosing failures is what the report is for.

Current workaround and why it is inadequate

Re-run with ignoreExitCode=true. This requires that the failure be reproducible, and it inverts the method's success semantics — the step then reports success on any exit code, so it cannot be left in place for a verification step whose whole purpose is to fail the run. It is a diagnostic detour, not a fix.

Observed on

  • swamp 20260830.034252.0-sha.43b19693
  • command/shell type version 2026.02.09.1
  • macOS 27.0 (Darwin 27.0.0), arm64
02Bog Flow
✓OPEN○TRIAGED○IN PROGRESS◉CLOSED

Closed

9/25/2026, 4:55:30 PM

No activity in this phase yet.

03Sludge Pulse
Editable. Press Enter to edit.

stack72 commented 9/25/2026, 4:55:28 PM

Hey @matthewbonig thanks for the detailed reproduction. This was fixed in v20260922.204047.0-sha.4957bcbd by #2555 (fix(models): keep a failing shell step's output instead of discarding it). Failed command/shell executions now persist the result and log artifacts before raising the non-zero-exit error, and attach those artifacts to the failed run. This restores the failure JSON/run record, queryable artifacts, log path, and downstream method-summary diagnostics. The current latest release, v20260925.163149.0-sha.bea803c9, includes the fix. Please upgrade and reopen if you can reproduce it there.

Sign in to post a ripple.