puppetlabs/puppet · error · Puppet::DevError

No command %{command} defined for provider %{provider}

Error message

No command %{command} defined for provider %{provider}

What it means

Providers declare external commands with commands :name => 'executable'; declarations live in a per-class @commands hash consulted with superclass lookup. Provider.command(name) returns the absolute path found via which(), and raises Puppet::DevError 'No command X defined for provider Y' when the name was never declared. This is a typo or missing declaration, distinct from a declared executable not being found on disk, which fails later in which().

Source

Thrown at lib/puppet/provider.rb:134

  # (see Puppet::Util::Execution.execpipe)
  def self.execpipe(*args, &block)
    Puppet::Util::Execution.execpipe(*args, &block)
  end

  # Returns the absolute path to the executable for the command referenced by the given name.
  # @raise [Puppet::DevError] if the name does not reference an existing command.
  # @return [String] the absolute path to the found executable for the command
  # @see which
  # @api public
  def self.command(name)
    name = name.intern

    command = @commands[name] if defined?(@commands)
    command = superclass.command(name) if !command && superclass.respond_to?(:command)

    unless command
      raise Puppet::DevError, _("No command %{command} defined for provider %{provider}") % { command: name, provider: self.name }
    end

    which(command)
  end

  # Confines this provider to be suitable only on hosts where the given commands are present.
  # Also see {Puppet::Confiner#confine} for other types of confinement of a provider by use of other types of
  # predicates.
  #
  # @note It is preferred if the commands are not entered with absolute paths as this allows puppet
  #   to search for them using the PATH variable.
  #
  # @param command_specs [Hash{String => String}] Map of name to command that the provider will
  #   be executing on the system. Each command is specified with a name and the path of the executable.
  # @return [void]
  # @see optional_commands
  # @api public
  #

View on GitHub (pinned to e227c27540)

Solutions

  1. Add the declaration at the top of the provider class: commands :list => 'lslv'.
  2. Fix the symbol passed to command() so it matches the declaration exactly.
  3. For shared command sets, declare them in a base provider class that both providers inherit from.

Example fix

# before (provider)
def self.instances
  cmd = command(:list)   # Puppet::DevError: No command list defined
end

# after
commands :list => 'lslv'
def self.instances
  cmd = command(:list)
end
Defensive patterns

Strategy: try-catch

Validate before calling

declared = {}
klass = provider.class
while klass && klass < Puppet::Provider
  declared.merge!(klass.instance_variable_get(:@commands) || {})
  klass = klass.superclass
end
fail 'undeclared command :list' unless declared.key?(:list)

Type guard

def declared_command?(provider_class, name)
  k = provider_class
  while k
    return true if (k.instance_variable_get(:@commands) || {}).key?(name)
    k = k.superclass
  end
  false
end

Try / catch

begin
  executable = self.class.command(:list)
rescue Puppet::DevError => e
  raise Puppet::Error, "#{self.class.name}: #{e.message} (declare with commands :list => 'lslv')"
end

Prevention

When it happens

Trigger: A provider method calls command(:list) but the class declares commands :lslv => 'lslv'; the symbol is misspelled on either side; the declaration exists on a sibling provider class instead of this class or an ancestor.

Common situations: Custom provider development; refactors that rename command aliases; copy-paste between providers where declarations were left behind.

Related errors


AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21). Data as JSON: /api/errors/6049f49329f85862. Report an issue: GitHub.