basecamp/kamal · error · ArgumentError

Raw is not compatible with interactive

Error message

Raw is not compatible with interactive

What it means

`kamal server exec CMD` runs a command directly on the configured hosts over SSH. --raw makes Kamal emit unmodified stdout (pipeable), while -i/--interactive allocates an interactive SSH session on the primary host. Since raw capture and a TTY-attached interactive session are incompatible, Kamal raises ArgumentError immediately when both flags are given, before any connection is made.

Source

Thrown at lib/kamal/cli/server.rb:9

class Kamal::Cli::Server < Kamal::Cli::Base
  desc "exec", "Run a custom command on the server (use --help to show options)"
  option :interactive, type: :boolean, aliases: "-i", default: false, desc: "Run the command interactively (use for console/bash)"
  option :raw, type: :boolean, default: false, desc: "Output raw, unmodified stdout"
  def exec(*cmd)
    raw = options[:raw]

    if raw && options[:interactive]
      raise ArgumentError, "Raw is not compatible with interactive"
    end

    with_raw_output(raw) do
      pre_connect_if_required

      cmd = Kamal::Utils.join_commands(cmd)
      hosts = KAMAL.hosts
      quiet = options[:quiet]

      case
      when options[:interactive]
        host = KAMAL.primary_host

        say "Running '#{cmd}' on #{host} interactively...", :magenta

        run_locally { exec KAMAL.server.run_over_ssh(cmd, host: host) }
      else
        say "Running '#{cmd}' on #{hosts.join(', ')}...", :magenta

View on GitHub (pinned to eee0083b38)

Solutions

  1. Choose one mode: `kamal server exec -i bash` for an interactive shell, `kamal server exec --raw CMD` for clean machine-readable output.
  2. Remove --raw from aliases that also set -i (or vice versa).
  3. For capturing interactive-program output, run the program non-interactively instead.

Example fix

# before
kamal server exec --raw -i bash
# after
kamal server exec -i bash
# or
kamal server exec --raw 'uptime'
Defensive patterns

Strategy: validation

Validate before calling

raw = ARGV.include?("--raw")
interactive = ARGV.include?("-i") || ARGV.include?("--interactive")
abort "--raw and -i are mutually exclusive" if raw && interactive
system("kamal server exec #{ARGV.join(' ')}")

Try / catch

begin
  Kamal::CLI::Server.new.invoke(:exec, [ "uptime" ], raw: true)
rescue ArgumentError => e
  warn "flag conflict: #{e.message}" if e.message.include?("not compatible")
end

Prevention

When it happens

Trigger: `kamal server exec --raw -i bash`, or any invocation where options[:raw] and options[:interactive] are both truthy — commonly a wrapper/alias that always adds one of the flags combined with a manual one-off use of the other.

Common situations: Piping `kamal server exec --raw uptime` into scripts while an alias injects -i; copy-pasted commands mixing console-style flags with capture-style flags; forgetting that --raw only matters for non-interactive output.

Related errors


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