capistrano/capistrano · error · NoMethodError

undefined method `execute' for main:Object

Error message

undefined method `execute' for main:Object

What it means

`execute` is an SSHKit method that runs a command on remote hosts through the backend established by Capistrano's `on(roles(...)) { ... }` scope. Outside an `on` block there is no backend, so Capistrano defines a top-level `execute` on the DSL purely to catch this beginner mistake: it prints a colored remediation hint to stderr (showing how to wrap the call in `on roles(:app)`) and then raises NoMethodError for main:Object. The error is intentional and the printed example above the exception is the actual fix.

Source

Thrown at lib/capistrano/dsl.rb:91

    # rubocop:enable Security/MarshalLoad

    def run_locally(&block)
      SSHKit::Backend::Local.new(&block).run
    end

    # Catch common beginner mistake and give a helpful error message on stderr
    def execute(*)
      file, line, = caller.first.split(":")
      colors = SSHKit::Color.new($stderr)
      $stderr.puts colors.colorize("Warning: `execute' should be wrapped in an `on' scope in #{file}:#{line}.", :red)
      $stderr.puts
      $stderr.puts "  task :example do"
      $stderr.puts colors.colorize("    on roles(:app) do", :yellow)
      $stderr.puts "      execute 'whoami'"
      $stderr.puts colors.colorize("    end", :yellow)
      $stderr.puts "  end"
      $stderr.puts
      raise NoMethodError, "undefined method `execute' for main:Object"
    end
  end
end
extend Capistrano::DSL

View on GitHub (pinned to b54b02fa0e)

Solutions

  1. Wrap the call in an `on` scope: `on roles(:app) do execute "whoami" end` (exactly as the stderr hint shows)
  2. For commands on the deploy machine itself, use `run_locally { execute "whoami" }` instead of `on`
  3. If the code lives in a helper, pass the backend explicitly or define the helper inside the `on` block so `execute` resolves against SSHKit::Backend

Example fix

# before
task :check do
  execute "whoami"   # NoMethodError: main:Object
end

# after
task :check do
  on roles(:app) do
    execute "whoami"
  end
end
Defensive patterns

Strategy: type-guard

Validate before calling

# assert a backend context exists before shelling out
if defined?(SSHKit::Backend) && SSHKit::Backend.current.is_a?(SSHKit::Backend::ConnectionPool::NilClass)
  raise "execute called outside an `on` scope"
end

Type guard

def inside_sshkit_backend?
  respond_to?(:execute) && !is_a?(Object) # top-level main:Object stub means we are OUTSIDE `on`
end
# practical check: run within `on`/`run_locally` blocks only
within_backend = self.class.ancestors.any? { |a| a.name.to_s.start_with?("SSHKit::Backend") }

Try / catch

begin
  execute "whoami"
rescue NoMethodError => e
  raise unless e.message.include?("execute' for main:Object")
  run_locally { execute "whoami" } # or wrap in on roles(:app)
end

Prevention

When it happens

Trigger: Calling `execute "whoami"`, `capture`, or `sudo` (which calls execute) directly inside a `task ... do` block without an enclosing `on` block; calling execute in a helper method invoked outside any `on` scope; running code at file-load time in deploy.rb outside a task/on.

Common situations: First Capistrano 3 project (Capistrano 2 allowed top-level `run`); refactoring a task and accidentally moving a line out of the `on` block; defining shared helper methods that call execute and invoking them from the wrong nesting level.

Related errors


AI-assisted analysis of capistrano/capistrano@b54b02fa0e (2026-08-21). Data as JSON: /api/errors/ed1bdcb1a706163d. Report an issue: GitHub.