{"record":{"id":"8d963755c3ad1c96","repo":"basecamp/kamal","slug":"raw-is-not-compatible-with-interactive-8d9637","errorCode":null,"errorMessage":"Raw is not compatible with interactive","messagePattern":"Raw is not compatible with interactive","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/kamal/cli/server.rb","lineNumber":9,"sourceCode":"class Kamal::Cli::Server < Kamal::Cli::Base\n  desc \"exec\", \"Run a custom command on the server (use --help to show options)\"\n  option :interactive, type: :boolean, aliases: \"-i\", default: false, desc: \"Run the command interactively (use for console/bash)\"\n  option :raw, type: :boolean, default: false, desc: \"Output raw, unmodified stdout\"\n  def exec(*cmd)\n    raw = options[:raw]\n\n    if raw && options[:interactive]\n      raise ArgumentError, \"Raw is not compatible with interactive\"\n    end\n\n    with_raw_output(raw) do\n      pre_connect_if_required\n\n      cmd = Kamal::Utils.join_commands(cmd)\n      hosts = KAMAL.hosts\n      quiet = options[:quiet]\n\n      case\n      when options[:interactive]\n        host = KAMAL.primary_host\n\n        say \"Running '#{cmd}' on #{host} interactively...\", :magenta\n\n        run_locally { exec KAMAL.server.run_over_ssh(cmd, host: host) }\n      else\n        say \"Running '#{cmd}' on #{hosts.join(', ')}...\", :magenta","sourceCodeStart":1,"sourceCodeEnd":27,"githubUrl":"https://github.com/basecamp/kamal/blob/eee0083b38661c3707c6b6052cc89e85038a096c/lib/kamal/cli/server.rb#L1-L27","documentation":"`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.","triggerScenarios":"`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.","commonSituations":"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.","solutions":["Choose one mode: `kamal server exec -i bash` for an interactive shell, `kamal server exec --raw CMD` for clean machine-readable output.","Remove --raw from aliases that also set -i (or vice versa).","For capturing interactive-program output, run the program non-interactively instead."],"exampleFix":"# before\nkamal server exec --raw -i bash\n# after\nkamal server exec -i bash\n# or\nkamal server exec --raw 'uptime'","handlingStrategy":"validation","validationCode":"raw = ARGV.include?(\"--raw\")\ninteractive = ARGV.include?(\"-i\") || ARGV.include?(\"--interactive\")\nabort \"--raw and -i are mutually exclusive\" if raw && interactive\nsystem(\"kamal server exec #{ARGV.join(' ')}\")","typeGuard":null,"tryCatchPattern":"begin\n  Kamal::CLI::Server.new.invoke(:exec, [ \"uptime\" ], raw: true)\nrescue ArgumentError => e\n  warn \"flag conflict: #{e.message}\" if e.message.include?(\"not compatible\")\nend","preventionTips":["Use --raw only for non-interactive scripted commands on servers.","Keep aliases from bundling -i with capture-style flags."],"tags":["kamal","server","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"}