puppetlabs/puppet · error · ArgumentError

Unknown service #{name}

Error message

Unknown service #{name}

What it means

Puppet::HTTP::Session#route_to resolves a named service; the name must be one of the frozen SERVICE_NAMES list (:ca, :fileserver, :puppet, :puppetserver, :report). Anything else — including Strings instead of Symbols, or legacy indirection names like :catalog or :facts — raises ArgumentError because no such routable service exists.

Source

Thrown at lib/puppet/http/session.rb:52

  # configuration file, this method always returns a Service with that host and
  # port. Otherwise, we walk the list of resolvers in priority order:
  #     - DNS SRV
  #     - Server List
  #     - Puppet server/port settings
  # If a given resolver fails to connect, it tries the next available resolver
  # until a successful connection is found and returned. The successful service
  # is cached and returned if `route_to` is called again.
  #
  # @param [Symbol] name the service to resolve
  # @param [URI] url optional explicit url to use, if it is already known
  # @param [Puppet::SSL::SSLContext] ssl_context ssl context to be
  #   used for connections
  #
  # @return [Puppet::HTTP::Service] the resolved service
  #
  # @api public
  def route_to(name, url: nil, ssl_context: nil)
    raise ArgumentError, "Unknown service #{name}" unless Puppet::HTTP::Service.valid_name?(name)

    # short circuit if explicit URL host & port given
    if url && !url.host.nil? && !url.host.empty?
      service = Puppet::HTTP::Service.create_service(@client, self, name, url.host, url.port)
      service.connect(ssl_context: ssl_context)
      return service
    end

    cached = @resolved_services[name]
    return cached if cached

    canceled = false
    canceled_handler = ->(cancel) { canceled = cancel }

    @resolvers.each do |resolver|
      Puppet.debug("Resolving service '#{name}' using #{resolver.class}")
      service = resolver.resolve(self, name, ssl_context: ssl_context, canceled_handler: canceled_handler)
      if service

View on GitHub (pinned to e227c27540)

Solutions

  1. Use one of :ca, :fileserver, :puppet, :puppetserver, :report — catalog/facts/node requests go through route_to(:puppet)
  2. Convert to a Symbol with name.to_sym before calling
  3. Guard with Puppet::HTTP::Service.valid_name?(name) when the name comes from external input

Example fix

# before (ruby)
service = session.route_to(:catalog)   # => ArgumentError: Unknown service catalog

# after
service = session.route_to(:puppet)
service.post_catalog(...)  # catalog is a method on the puppet service
Defensive patterns

Strategy: validation

Validate before calling

# ruby
raise ArgumentError, "unroutable service #{name}" unless Puppet::HTTP::Service.valid_name?(name)
service = session.route_to(name)

Type guard

def routable_service?(name)
  Puppet::HTTP::Service.valid_name?(name)  # :ca :fileserver :puppet :puppetserver :report
end

Try / catch

begin
  session.route_to(name)
rescue ArgumentError
  name = :puppet  # catalog/facts/node live under :puppet
  retry
end

Prevention

When it happens

Trigger: session.route_to('puppet') with a String instead of a Symbol; session.route_to(:catalog) or :facts — catalog and facts are indirections served by the :puppet service, not service names; typos like :reports.

Common situations: Migrating from the old Puppet::Network::HTTP connection API where any REST endpoint was reachable; assuming every indirection name is routable; passing a name read from config or templates that arrives as a String.

Related errors


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