benbjohnson/litestream · error

cannot start exec command: %w

Error message

cannot start exec command: %w

What it means

After parsing the exec string, Run builds an exec.CommandContext and starts the subprocess. If cmd.Start fails (binary not found in PATH, not executable, missing interpreter), the error is wrapped as "cannot start exec command". Litestream exits rather than replicating without its supervised process.

Source

Thrown at cmd/litestream/replicate.go:381

			if err := http.ListenAndServe(c.Config.Addr, nil); err != nil {
				slog.Error("cannot start metrics server", "error", err)
			}
		}()
	}

	// Parse exec commands args & start subprocess.
	if c.Config.Exec != "" {
		execArgs, err := shellwords.Parse(c.Config.Exec)
		if err != nil {
			return fmt.Errorf("cannot parse exec command: %w", err)
		}

		c.cmd = exec.CommandContext(ctx, execArgs[0], execArgs[1:]...)
		c.cmd.Env = os.Environ()
		c.cmd.Stdout = os.Stdout
		c.cmd.Stderr = os.Stderr
		if err := c.cmd.Start(); err != nil {
			return fmt.Errorf("cannot start exec command: %w", err)
		}
		go func() { c.execCh <- c.cmd.Wait() }()
	} else if c.once {
		// Run one-shot replication in a goroutine so the caller can wait on execCh.
		go c.runOnce(ctx)
	}

	return nil
}

// runOnce performs one-shot replication for all databases.
// It syncs all databases, optionally takes snapshots, and enforces retention.
func (c *ReplicateCommand) runOnce(ctx context.Context) {
	var err error
	defer func() { c.execCh <- err }()

	for _, db := range c.Store.DBs() {
		slog.Info("syncing database", "path", db.Path())

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Verify the command exists in PATH for the litestream process (`which <cmd>` as the same user)
  2. Use an absolute path in exec and ensure the execute bit is set (chmod +x)
  3. Check the script's shebang interpreter exists in the image
  4. Ensure litestream runs as a user with permission to execute the command

Example fix

// before (config)
exec: myapp
// after
exec: /usr/local/bin/myapp  # chmod +x /usr/local/bin/myapp
Defensive patterns

Strategy: try-catch

Validate before calling

CMD=$(yq '.exec' litestream.yml)
BIN=$(echo "$CMD" | awk '{print $1}')
command -v "$BIN" >/dev/null || { echo "exec binary not found: $BIN" >&2; exit 1; }
[ -x "$BIN" ] || { echo "exec binary not executable: $BIN" >&2; exit 1; }

Try / catch

if err := cmd.Run(ctx); err != nil {
    if strings.Contains(err.Error(), "cannot start exec command") {
        log.Fatalf("exec subprocess failed to start: %v", err)
    }
    return err
}

Prevention

When it happens

Trigger: `exec.CommandContext(...).Start()` returns error in Run — command name not in PATH, file lacks execute permission, shebang interpreter missing, or working directory invalid.

Common situations: Container images where the app binary isn't installed or isn't in PATH; relative paths resolved against an unexpected working directory; permission bits lost after copying files.

Related errors


AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06). Data as JSON: /api/errors/07533565a6e179ae. Report an issue: GitHub.