{"record":{"id":"3e9728fec76249e4","repo":"basecamp/kamal","slug":"no-command-provided-you-must-specify-a-command-to","errorCode":null,"errorMessage":"No command provided. You must specify a command to execute.","messagePattern":"No command provided\\. You must specify a command to execute\\.","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/kamal/cli/app.rb","lineNumber":109,"sourceCode":"  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\n      when options[:interactive] && options[:reuse]\n        say \"Get current version of running container...\", :magenta unless options[:version]\n        using_version(options[:version] || current_running_version) do |version|\n          say \"Launching interactive command with version #{version} via SSH from existing container on #{KAMAL.primary_host}...\", :magenta\n          run_locally { exec KAMAL.app(role: KAMAL.primary_role, host: KAMAL.primary_host).execute_in_existing_container_over_ssh(cmd, env: env) }\n        end\n\n      when options[:interactive]","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/basecamp/kamal/blob/eee0083b38661c3707c6b6052cc89e85038a096c/lib/kamal/cli/app.rb#L91-L127","documentation":"`kamal app exec CMD...` requires at least one positional argument: the command to run inside the app container. Thor collects variadic args, so invoking exec with no arguments parses fine, and Kamal then explicitly raises ArgumentError ('No command provided. You must specify a command to execute.') after the flag-compatibility checks and before connecting to any host.","triggerScenarios":"Running bare `kamal app exec`, or `kamal app exec --reuse` with only options and no positional command, or quoting mistakes that make the command disappear (e.g. an empty string variable: `kamal app exec \"$CMD\"` with CMD unset).","commonSituations":"Scripts building the command dynamically from an env var that is empty in CI; forgetting that unlike `kamal app exec -i bash`, some shells need the command quoted as one arg; running exec in a loop where one iteration has no command.","solutions":["Pass an explicit command: `kamal app exec --reuse 'ls -la /app'`.","If the command comes from a variable, default or fail fast before calling kamal: `[ -n \"$CMD\" ] || { echo 'CMD empty'; exit 1; }`.","Quote multi-word commands so they arrive as args rather than being globbed/split by the shell."],"exampleFix":"# before\nCMD=\"\" kamal app exec --reuse \"$CMD\"\n# after\nCMD=\"rails db:migrate\" kamal app exec --reuse $CMD\n# or fail fast in the caller:\n[ -n \"$CMD\" ] || { echo \"CMD is empty\" >&2; exit 1; }\nkamal app exec --reuse $CMD","handlingStrategy":"validation","validationCode":"cmd = ENV[\"APP_EXEC_CMD\"]\nabort \"APP_EXEC_CMD is empty; refusing to run kamal app exec\" if cmd.to_s.strip.empty?\nsystem(\"kamal app exec --reuse #{cmd}\")","typeGuard":null,"tryCatchPattern":"begin\n  Kamal::CLI::App.new.invoke(:exec, cmd_args)\nrescue ArgumentError => e\n  raise unless e.message.include?(\"No command provided\")\n  abort \"build the command before calling exec\"\nend","preventionTips":["Always pass a literal command string in scripts; fail fast when it comes from a variable.","Prefer full, quoted commands (`'rails db:migrate'`) so shells cannot swallow them."],"tags":["kamal","app","exec","cli","missing-argument"],"backgroundTag":"missing-required-argument","analyzedSha":"eee0083b38661c3707c6b6052cc89e85038a096c","analyzedAt":"2026-08-21T15:17:22.045Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}