Skip to main content
← Back to list
01Issue
BugShippedSwamp CLI
Assigneesstack72

Relationships

#1013 command/shell: step with timeout + a surviving child process reports exit -1 despite command exiting 0

Opened by atalanta · 7/7/2026· Shipped 7/7/2026

Summary

A command/shell (command/shell type, typeVersion 2026.02.09.1) execute step run inside a workflow reports Command exited with code -1 (step failed) when BOTH conditions hold:

  1. the step has a timeout set, AND
  2. the command leaves a background/child process alive at the moment it exits.

The command's own exit code is 0 (the recorded result.exitCode is 0), and the same command succeeds when run without a timeout. The -1 therefore comes from the timeout wrapper's process handling, not from the command.

Environment

  • swamp: 20260626.005336.0-sha.f302d18e
  • command/shell typeVersion: 2026.02.09.1
  • macOS (Darwin), zsh

Minimal reproduction

A one-step workflow calling command/shell.execute:

jobs:
  - name: main
    steps:
      - name: spawn
        task:
          type: model_method
          modelIdOrName: <a command/shell instance>
          methodName: execute
          inputs:
            run: "sleep 2 & exit 0"    # backgrounds a child, then exits 0
            workingDir: '.'
            timeout: 900
            ignoreExitCode: false

Run it: the step FAILS with Command exited with code -1.

Remove the timeout: 900 line (leave everything else): the step SUCCEEDS.

Characterisation (isolated)

  • timeout + trivial command, no child (echo hi; exit 0) -> SUCCEEDS
  • timeout + backgrounded child (sleep 2 & exit 0) -> FAILS -1
  • timeout + cargo test (spawns test/subprocesses) -> FAILS -1
  • NO timeout + any of the above -> SUCCEEDS
  • The recorded result.exitCode is 0 even when the step is marked failed.

Expected

With ignoreExitCode: false, the step outcome should reflect the command's own exit status (0 -> success). A timeout that is not exceeded should not change the outcome, regardless of whether the command spawned a child that is still alive at exit.

Likely cause / hypothesis

The timeout path appears to run the command in a process group and, on completion, waits on / signals the group and reads a child's status (or the group-kill signal) as the step's exit code (-1) instead of the direct child's exit code. Non-timeout path does not, hence the difference.

Impact

Any command/shell step with a timeout that runs a test suite or tool which spawns subprocesses (very common) fails spuriously, so timeout cannot be used as a guard on such steps. Workaround: omit timeout (loses the guard) or invoke the model method directly outside a workflow (loses gating).

02Bog Flow
OPENTRIAGEDIN PROGRESSSHIPPED+ 1 MOREASSIGNED+ 5 MOREREVIEW+ 4 MOREPR_MERGED+ 1 MORECONTRIBUTOR_NOTIFIED

Shipped

7/7/2026, 8:28:41 PM

Click a lifecycle step above to view its details.

03Sludge Pulse
stack72 assigned stack727/7/2026, 1:55:18 PM
Editable. Press Enter to edit.

stack72 commented 7/7/2026, 8:28:49 PM

Thanks @atalanta for reporting this! The fix has been merged and a release is on its way. We appreciate your contribution to swamp.

Sign in to post a ripple.