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]
caseView on GitHub (pinned to eee0083b38)
Solutions
- 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.
- For long-running one-off work in the running container, use --reuse without detach (SSH session waits) or detach without reuse.
- 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
- One-off jobs -> --detach; jobs in the running container -> --reuse; shells -> -i. Pick one per invocation.
- When composing kamal commands in scripts, build flags from an explicit mode variable instead of booleans.
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
- Raw is not compatible with #{incompatible_options.join(" or
- Raw is not compatible with interactive
- No command provided. You must specify a command to execute.
- Raw is not compatible with interactive
- Deploy lock held manually, not waiting. Run 'kamal lock help
AI-assisted analysis of basecamp/kamal@eee0083b38 (2026-08-21).
Data as JSON: /api/errors/00101aa4333677a4.
Report an issue: GitHub.