basecamp/kamal · error · ArgumentError

No --roles match for #{role_names.join(',')}

Error message

No --roles match for #{role_names.join(',')}

What it means

Kamal::Commander#specific_roles= is called when you pass the global --roles filter (e.g. `kamal deploy --roles web,workers` or programmatic use). It filters config.roles through Kamal::Utils.filter_specific_items, which uses File.fnmatch with FNM_EXTGLOB — so filters support fnmatch patterns like 'web*' or extended globs, matched against role names. If no configured role matches any filter, the filtered list is empty and Kamal raises ArgumentError ('No --roles match for ...').

Source

Thrown at lib/kamal/commander.rb:59

  def configured?
    @config || @config_kwargs
  end

  def specific_primary!
    @specifics = nil
    if specific_roles.present?
      self.specific_hosts = [ specific_roles.first.primary_host ]
    else
      self.specific_hosts = [ config.primary_host ]
    end
  end

  def specific_roles=(role_names)
    @specifics = nil
    @specific_roles = if role_names.present?
      filtered = Kamal::Utils.filter_specific_items(role_names, config.roles)
      raise ArgumentError, "No --roles match for #{role_names.join(',')}" if filtered.empty?
      filtered
    end
  end

  def specific_hosts=(hosts)
    @specifics = nil
    @specific_hosts = if hosts.present?
      filtered = Kamal::Utils.filter_specific_items(hosts, config.all_hosts)
      raise ArgumentError, "No --hosts match for #{hosts.join(',')}" if filtered.empty?
      filtered
    end
  end

  def with_specific_hosts(hosts)
    original_hosts, self.specific_hosts = specific_hosts, hosts
    yield
  ensure
    self.specific_hosts = original_hosts

View on GitHub (pinned to eee0083b38)

Solutions

  1. List the roles actually defined in your config (grep `servers:` block names in config/deploy*.yml or run `kamal app details`) and correct the --roles value to match.
  2. If you meant a pattern, ensure it is fnmatch-compatible ('web*' works; regex like 'web|workers' does not).
  3. Update CI/alias scripts after renaming roles in deploy.yml.
  4. If targeting a specific host instead, use --hosts with a hostname from config.

Example fix

# before
# config/deploy.yml defines servers: web: ..., workers: ...
kamal deploy --roles webb      # typo -> No --roles match for webb
# after
kamal deploy --roles web
# patterns are fnmatch-style:
kamal deploy --roles 'w*'
Defensive patterns

Strategy: validation

Validate before calling

require "yaml"
configured = YAML.load_file("config/deploy.yml")["servers"].keys # role names
requested = %w[web workers]
unknown = requested.reject { |r| configured.include?(r) || configured.any? { |c| File.fnmatch(r, c) } }
abort "no roles match #{unknown.join(',')}" unless unknown.empty?
system("kamal deploy --roles #{requested.join(',')}")

Type guard

def roles_match?(filters, configured_roles)
  filters.any? { |f| configured_roles.any? { |r| File.fnmatch(f, r, File::FNM_EXTGLOB) } }
end

Try / catch

begin
  Kamal::Commander.new.specific_roles = %w[web]
rescue ArgumentError => e
  raise unless e.message.include?("--roles")
  abort "check role names in config/deploy.yml: #{e.message}"
end

Prevention

When it happens

Trigger: `kamal app boot --roles web2` when config defines only web/workers; using a glob that matches nothing (`--roles job*` with no job roles); quoting issues so the filter string includes stray characters; renaming roles in deploy.yml but running old scripts with the previous names.

Common situations: Role renamed during config refactor while CI pipelines still pass the old --roles value; typos in role names; destination configs (deploy.staging.yml) defining a different role subset than production; glob syntax expectations differing from fnmatch semantics.

Related errors


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