{"record":{"id":"00101aa4333677a4","repo":"basecamp/kamal","slug":"detach-is-not-compatible-with-incompatible-optio","errorCode":null,"errorMessage":"Detach is not compatible with #{incompatible_options.join(\" or \")}","messagePattern":"Detach is not compatible with #(.+?)","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/kamal/cli/app.rb","lineNumber":101,"sourceCode":"  desc \"details\", \"Show details about app containers\"\n  def details\n    quiet = options[:quiet]\n    on_roles(KAMAL.roles, hosts: KAMAL.app_hosts) do |host, role|\n      puts_by_host host, capture_with_info(*KAMAL.app(role: role, host: host).info), quiet: quiet\n    end\n  end\n\n  desc \"exec [CMD...]\", \"Execute a custom command on servers within the app container (use --help to show options)\"\n  option :interactive, aliases: \"-i\", type: :boolean, default: false, desc: \"Execute command over ssh for an interactive shell (use for console/bash)\"\n  option :reuse, type: :boolean, default: false, desc: \"Reuse currently running container instead of starting a new one\"\n  option :env, aliases: \"-e\", type: :hash, desc: \"Set environment variables for the command\"\n  option :detach, type: :boolean, default: false, desc: \"Execute command in a detached container\"\n  option :raw, type: :boolean, default: false, desc: \"Output raw, unmodified stdout\"\n  def exec(*cmd)\n    raw = options[:raw]\n\n    if (incompatible_options = [ :interactive, :reuse ].select { |key| options[:detach] && options[key] }.presence)\n      raise ArgumentError, \"Detach is not compatible with #{incompatible_options.join(\" or \")}\"\n    end\n\n    if raw && (incompatible_options = [ :interactive, :detach ].select { |key| options[key] }.presence)\n      raise ArgumentError, \"Raw is not compatible with #{incompatible_options.join(\" or \")}\"\n    end\n\n    if cmd.empty?\n      raise ArgumentError, \"No command provided. You must specify a command to execute.\"\n    end\n\n    with_raw_output(raw) do\n      pre_connect_if_required\n\n      cmd = Kamal::Utils.join_commands(cmd)\n      env = options[:env]\n      detach = options[:detach]\n      quiet = options[:quiet]\n      case","sourceCodeStart":83,"sourceCodeEnd":119,"githubUrl":"https://github.com/basecamp/kamal/blob/eee0083b38661c3707c6b6052cc89e85038a096c/lib/kamal/cli/app.rb#L83-L119","documentation":"`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.","triggerScenarios":"`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').","commonSituations":"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.","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."],"exampleFix":"# before\nkamal app exec --detach --reuse rails db:migrate\n# after\nkamal app exec --reuse rails db:migrate\n# or intentionally start a detached one-off container:\nkamal app exec --detach rails db:migrate","handlingStrategy":"validation","validationCode":"opts = { detach: Flag.d?, interactive: Flag.i?, reuse: Flag.reuse? } # however you parse\nconflicts = %i[interactive reuse].select { |k| opts[:detach] && opts[k] }\nabort \"--detach conflicts with #{conflicts.join(', ')}\" unless conflicts.empty?","typeGuard":null,"tryCatchPattern":"begin\n  Kamal::CLI::App.new.invoke(:exec, [ \"rails\", \"db:migrate\" ], detach: true)\nrescue ArgumentError => e\n  retry_without = e.message[/Detach is not compatible with (.+)/, 1]\n  warn \"remove #{retry_without} when using --detach\"\nend","preventionTips":["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."],"tags":["kamal","app","exec","cli","argument-conflict"],"backgroundTag":"mutually-exclusive-flags","analyzedSha":"eee0083b38661c3707c6b6052cc89e85038a096c","analyzedAt":"2026-08-21T15:17:22.045Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}