benbjohnson/litestream · error

cannot parse exec command: %w

Error message

cannot parse exec command: %w

What it means

The `-exec` option value is parsed with shellwords.Parse to split it into a command and arguments. If the string has unbalanced quotes or malformed shell-like syntax, parsing fails and Run returns this error with the underlying shellwords error wrapped.

Source

Thrown at cmd/litestream/replicate.go:373

			return fmt.Errorf("must specify port for bind address: %q", c.Config.Addr)
		} else if host == "" {
			hostport = net.JoinHostPort("localhost", port)
		}

		slog.Info("serving metrics on", "url", fmt.Sprintf("http://%s/metrics", hostport))
		go func() {
			http.Handle("/metrics", promhttp.Handler())
			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
}

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Fix quoting in the exec string (balanced single/double quotes, no smart quotes)
  2. Simplify by pointing exec at a shell script: `exec: /bin/run-app.sh`
  3. Test the string with a shell-words parser or `sh -c` equivalent before adding it to config

Example fix

// before (config)
exec: myapp --name "prod db
// after
exec: myapp --name "prod db"
Defensive patterns

Strategy: validation

Validate before calling

# sanity-check exec quoting (shell-words style)
python3 - "$EXEC" <<'EOF'
import sys
try:
    import shlex; shlex.split(sys.argv[1])
except ValueError as e:
    sys.exit(f"bad exec string: {e}")
EOF

Prevention

When it happens

Trigger: `-exec` / config `exec:` value containing unmatched quotes, dangling escape characters, or other shell-words syntax errors, e.g. `exec: "myapp --flag 'oops"`.

Common situations: YAML quoting interacting with inner quotes; copy-pasted commands with smart quotes; complex commands better suited to a wrapper script.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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