basecamp/kamal · error · ArgumentError

Detach is not compatible with #{incompatible_options.join("

Error message

Detach is not compatible with #{incompatible_options.join(" or ")}

What it means

`kamal app exec CMD` supports --detach (run the command in a new detached container), -i/--interactive (SSH-attached TTY), and --reuse (run inside the currently running container). Detach conflicts with both interactive and reuse: a detached container cannot have an attached TTY, and it starts a new container rather than reusing one. Kamal collects the offending flags and raises ArgumentError listing them before executing anything.

Source

Thrown at lib/kamal/cli/app.rb:101

  desc "details", "Show details about app containers"
  def details
    quiet = options[:quiet]
    on_roles(KAMAL.roles, hosts: KAMAL.app_hosts) do |host, role|
      puts_by_host host, capture_with_info(*KAMAL.app(role: role, host: host).info), quiet: quiet
    end
  end

  desc "exec [CMD...]", "Execute a custom command on servers within the app container (use --help to show options)"
  option :interactive, aliases: "-i", type: :boolean, default: false, desc: "Execute command over ssh for an interactive shell (use for console/bash)"
  option :reuse, type: :boolean, default: false, desc: "Reuse currently running container instead of starting a new one"
  option :env, aliases: "-e", type: :hash, desc: "Set environment variables for the command"
  option :detach, type: :boolean, default: false, desc: "Execute command in a detached container"
  option :raw, type: :boolean, default: false, desc: "Output raw, unmodified stdout"
  def exec(*cmd)
    raw = options[:raw]

    if (incompatible_options = [ :interactive, :reuse ].select { |key| options[:detach] && options[key] }.presence)
      raise ArgumentError, "Detach is not compatible with #{incompatible_options.join(" or ")}"
    end

    if raw && (incompatible_options = [ :interactive, :detach ].select { |key| options[key] }.presence)
      raise ArgumentError, "Raw is not compatible with #{incompatible_options.join(" or ")}"
    end

    if cmd.empty?
      raise ArgumentError, "No command provided. You must specify a command to execute."
    end

    with_raw_output(raw) do
      pre_connect_if_required

      cmd = Kamal::Utils.join_commands(cmd)
      env = options[:env]
      detach = options[:detach]
      quiet = options[:quiet]
      case

View on GitHub (pinned to eee0083b38)

Solutions

  1. Pick one execution mode: `kamal app exec --reuse CMD` to run in the running container, or `kamal app exec --detach CMD` to start a detached one-off container.
  2. For long-running one-off work in the running container, use --reuse without detach (SSH session waits) or detach without reuse.
  3. Audit wrapper scripts for hardcoded flags before appending new ones.

Example fix

# before
kamal app exec --detach --reuse rails db:migrate
# after
kamal app exec --reuse rails db:migrate
# or intentionally start a detached one-off container:
kamal app exec --detach rails db:migrate
Defensive patterns

Strategy: validation

Validate before calling

opts = { detach: Flag.d?, interactive: Flag.i?, reuse: Flag.reuse? } # however you parse
conflicts = %i[interactive reuse].select { |k| opts[:detach] && opts[k] }
abort "--detach conflicts with #{conflicts.join(', ')}" unless conflicts.empty?

Try / catch

begin
  Kamal::CLI::App.new.invoke(:exec, [ "rails", "db:migrate" ], detach: true)
rescue ArgumentError => e
  retry_without = e.message[/Detach is not compatible with (.+)/, 1]
  warn "remove #{retry_without} when using --detach"
end

Prevention

When it happens

Trigger: `kamal app exec --detach -i bash`, `kamal app exec --detach --reuse rails db:migrate`, or any invocation where options[:detach] is true together with options[:interactive] or options[:reuse]. The incompatible flag names are interpolated into the message ('interactive or reuse').

Common situations: Adding --detach to a command line copied from runbook docs that already includes --reuse; scripts that always pass --reuse for migrations accidentally combined with a detach flag; forgetting that -d/-i shorthand meanings differ from docker run.

Related errors


AI-assisted analysis of basecamp/kamal@eee0083b38 (2026-08-21). Data as JSON: /api/errors/00101aa4333677a4. Report an issue: GitHub.