puppetlabs/puppet · error · Puppet::Network::HTTP::Error::HTTPBadRequestError

Indirection '%{indirection_name}' does not match url prefix

Error message

Indirection '%{indirection_name}' does not match url prefix '%{url_prefix}'

What it means

Puppet's indirected REST routes validate that the URL prefix of a request matches the indirection being requested. IndirectionType maps certificate, certificate_request, certificate_status and certificate_revocation_list to the 'puppet-ca/v1' prefix and everything else to 'puppet/v3'. When the handler receives a mismatched pair, it rejects the request with HTTP 400 before the indirection is ever consulted.

Source

Thrown at lib/puppet/network/http/api/indirected_routes.rb:74

  end

  def uri2indirection(http_method, uri, params)
    # the first field is always nil because of the leading slash,
    indirection_type, version, indirection_name, key = uri.split("/", 5)[1..]
    url_prefix = "/#{indirection_type}/#{version}"
    environment = params.delete(:environment)

    if indirection_name !~ /^\w+$/
      raise Puppet::Network::HTTP::Error::HTTPBadRequestError, _("The indirection name must be purely alphanumeric, not '%{indirection_name}'") % { indirection_name: indirection_name }
    end

    # this also depluralizes the indirection_name if it is a search
    method = indirection_method(http_method, indirection_name)

    # check whether this indirection matches the prefix and version in the
    # request
    if url_prefix != IndirectionType.url_prefix_for(indirection_name)
      raise Puppet::Network::HTTP::Error::HTTPBadRequestError, _("Indirection '%{indirection_name}' does not match url prefix '%{url_prefix}'") % { indirection_name: indirection_name, url_prefix: url_prefix }
    end

    indirection = Puppet::Indirector::Indirection.instance(indirection_name.to_sym)
    unless indirection
      raise Puppet::Network::HTTP::Error::HTTPNotFoundError.new(
        _("Could not find indirection '%{indirection_name}'") % { indirection_name: indirection_name },
        Puppet::Network::HTTP::Issues::HANDLER_NOT_FOUND
      )
    end

    unless environment
      raise Puppet::Network::HTTP::Error::HTTPBadRequestError, _("An environment parameter must be specified")
    end

    unless Puppet::Node::Environment.valid_name?(environment)
      raise Puppet::Network::HTTP::Error::HTTPBadRequestError, _("The environment must be purely alphanumeric, not '%{environment}'") % { environment: environment }
    end

View on GitHub (pinned to e227c27540)

Solutions

  1. Use the correct prefix: certificate, certificate_request, certificate_status, certificate_revocation_list go under /puppet-ca/v1/..., all other indirections under /puppet/v3/...
  2. Check proxy and rewrite rules in front of the Puppet master so the full /puppet/v3 or /puppet-ca/v1 path reaches the handler intact
  3. When writing a custom handler, register routes via Puppet::Network::HTTP::Route.path('/puppet') vs Route.path('/puppet-ca') to match the indirections you serve
  4. Enable Puppet debug logging ('Routes Registered') to see the exact url_prefix the handler receives for the failing request

Example fix

# before
curl -k "https://puppet.example.com:8140/puppet/v3/certificate/ca?environment=production"

# after
# -k "https://puppet.example.com:8140/puppet-ca/v1/certificate/ca?environment=production"
Defensive patterns

Strategy: validation

Validate before calling

CA_INDIRECTIONS = %w[certificate certificate_request certificate_status certificate_revocation_list].freeze

def prefix_for(indirection)
  CA_INDIRECTIONS.include?(indirection) ? 'puppet-ca/v1' : 'puppet/v3'
end

def indirected_uri(indirection, key)
  "/#{prefix_for(indirection)}/#{indirection}/#{key}"
end

Prevention

When it happens

Trigger: Requesting a CA indirection under the master prefix, e.g. GET /puppet/v3/certificate/ca, or a master indirection under the CA prefix, e.g. GET /puppet-ca/v1/catalog/mynode. Also produced by custom mounts or reverse proxies that rewrite the /puppet/v3 or /puppet-ca/v1 path segments so url_prefix no longer equals IndirectionType.url_prefix_for(indirection_name).

Common situations: Hand-written curl or rest-client calls that hardcode '/puppet' for every endpoint; middleware/proxies stripping or rewriting the versioned path prefix; scripts ported from the legacy v2 URL scheme (/production/catalog/...) that guess the new prefix; custom Rack/WEBrick servers registering routes with the wrong prefix for the served indirections.

Related errors


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