basecamp/kamal · error · ArgumentError

Raw is not compatible with #{incompatible_options.join(" or

Error message

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

What it means

`kamal app exec CMD` supports --raw to emit unmodified stdout suitable for piping. Raw capture requires a non-interactive, attached execution, so it cannot be combined with -i/--interactive (TTY session mangles output) or --detach (output goes to container logs, not stdout). Kamal checks this after the detach check and raises ArgumentError naming the conflicting flags.

Source

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

      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
      when options[:interactive] && options[:reuse]
        say "Get current version of running container...", :magenta unless options[:version]
        using_version(options[:version] || current_running_version) do |version|
          say "Launching interactive command with version #{version} via SSH from existing container on #{KAMAL.primary_host}...", :magenta

View on GitHub (pinned to eee0083b38)

Solutions

  1. Remove -i or --detach: `kamal app exec --raw CMD` alone is the supported way to get clean output.
  2. For a detached command's output, read it afterwards with `kamal app logs` or `docker logs`.
  3. For an interactive shell, drop --raw: `kamal app exec -i bash`.

Example fix

# before
kamal app exec --raw -i rails console
# after
kamal app exec -i rails console
# or for scriptable, clean stdout:
kamal app exec --reuse --raw rails runner 'puts User.count'
Defensive patterns

Strategy: validation

Validate before calling

raw, interactive, detach = ARGV.any? { |a| a == "--raw" }, ARGV.include?("-i"), ARGV.include?("--detach")
abort "--raw cannot combine with -i/--interactive or --detach" if raw && (interactive || detach)

Try / catch

begin
  Kamal::CLI::App.new.invoke(:exec, [ "rails", "runner", "puts 1" ], raw: true, reuse: true)
rescue ArgumentError => e
  warn "flag conflict: #{e.message}" if e.message.start_with?("Raw is not compatible")
end

Prevention

When it happens

Trigger: `kamal app exec --raw -i bash`, `kamal app exec --raw --detach rails runner script.rb`, or any invocation where options[:raw] is truthy together with options[:interactive] or options[:detach].

Common situations: Piping a command's output into jq/grep with --raw while leaving -i on the line from earlier console use; adding --raw to a detached job expecting to capture logs; aliases that combine flags for convenience.

Related errors


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