Skip to content

[Bug]: compose.sh swallows the real exit code — autostart silently logs "success" for stacks that failed to start #135

Description

@FudiHub

Version

2026.07.02

Unraid Version

7.3.1

Bug Description

Summary

compose.sh always returns exit code 0 for the up action (and likely down/stop/pull), regardless of whether the underlying docker compose command actually succeeded. As a result, the autostart handler (event/docker_started) logs "Stack X started successfully" even when the stack failed to start, and the real error output from docker compose is discarded instead of being logged.

Root cause

In compose.sh, the up) case branch does roughly:

"${compose_base[@]}" -p "$name" up "${cmd_args[@]}" -d
exit_code=$?
 
if [ $exit_code -eq 0 ]; then
  ...
  echo "✓ Stack $name started successfully"
else
  save_result "failed" $exit_code "up"
  log_msg "ERROR" "Failed to start stack $name (exit code: $exit_code)"
  echo ""
  echo "✗ Stack $name failed to start (exit code: $exit_code)"
fi
;;

There is no explicit exit $exit_code after this block, or anywhere else in the script for this code path. In bash, a script with no explicit exit returns the exit status of the last command it ran — and in both branches that's an echo, which always returns 0. So compose.sh itself exits 0 even on the failure path.

Impact

event/docker_started uses the wrapper's own exit code to decide success/failure:

output=$(timeout "$STARTUP_TIMEOUT" "${cmd_args[@]}" 2>&1)
exit_code=$?
 
if [ $exit_code -eq 0 ]; then
    log "Stack $stack_name started successfully in ${duration}s"
    return 0
...

Because compose.sh always returns 0, this success branch runs even when docker compose up actually failed. Two consequences:

  1. The log falsely reports success for stacks that never started.
  2. The branch that logs the last 5 lines of docker compose output for debugging never executes, so the real failure reason is silently thrown away.

Expected Behavior

Errors should be logged and the script should so stack that did not start as failed not as successful.

Suggested fix

Capture the real exit code and propagate it explicitly instead of relying on the last command's status — e.g. exit "$exit_code" at the end of each case branch that can fail, or at minimum at the end of the script.

Workaround used

Manually patched compose.sh locally by appending an explicit exit after the closing esac:

printf '\nexit "${exit_code:-0}"\n' >> compose.sh

This surfaced the real errors for two stacks that had been silently logged as "started successfully":

audiobookshelf: open /boot/config/plugins/compose.manager/projects/audiobookshelf/compose.override.yaml: no such file or directory
photoprism: Error response from daemon: failed to set up container networking: network <id> not found

Steps to Reproduce

Boot a system with a stack whose docker compose up fails (e.g. a missing override file, a stale network reference). Syslog shows both lines, back to back, for the same stack:

[ERROR] [compose] Failed to start stack <name> (exit code: 1)
[INFO] [autostart] Stack <name> started successfully in 0s

Relevant Logs

Compose

Additional Context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions