puppetlabs/puppet · error · Puppet::HTTP::ProtocolError

No content type in http response; cannot parse

Error message

No content type in http response; cannot parse

What it means

All Puppet::HTTP::Service subclasses (CA, fileserver, compiler, report) pick a deserializer for a response from its Content-Type header via formatter_for_response. A 2xx response with no Content-Type cannot be mapped to any format, so Puppet::HTTP::ProtocolError is raised before body parsing.

Source

Thrown at lib/puppet/http/service.rb:132

      end
    end
    modified_headers
  end

  def build_url(api, server, port)
    URI::HTTPS.build(host: server,
                     port: port,
                     path: api).freeze
  end

  def get_mime_types(model)
    network_formats = model.supported_formats - EXCLUDED_FORMATS
    network_formats.map { |f| model.get_format(f).mime }
  end

  def formatter_for_response(response)
    header = response['Content-Type']
    raise Puppet::HTTP::ProtocolError, _("No content type in http response; cannot parse") unless header

    header.gsub!(/\s*;.*$/, '') # strip any charset

    formatter = Puppet::Network::FormatHandler.mime(header)
    raise Puppet::HTTP::ProtocolError, "Content-Type is unsupported" if EXCLUDED_FORMATS.include?(formatter.name)

    formatter
  end

  def serialize(formatter, object)
    formatter.render(object)
  rescue => err
    raise Puppet::HTTP::SerializationError.new("Failed to serialize #{object.class} to #{formatter.name}: #{err.message}", err)
  end

  def serialize_multiple(formatter, object)
    formatter.render_multiple(object)
  rescue => err

View on GitHub (pinned to e227c27540)

Solutions

  1. Reproduce with curl -i <url> and confirm the missing header at the client's vantage point.
  2. Fix the origin/proxy to send the correct type (application/json, application/vnd.puppet.pson+json, etc.).
  3. Verify the URL actually targets a puppet route (e.g. /puppet/v3/...) and the right port (8140).
  4. For custom puppetserver endpoints, set response content type explicitly in the Ruby handler.

Example fix

# custom puppetserver route - before
get '/custom/status' do |req, res|
  res.body = '{}'
end

# after
get '/custom/status' do |req, res|
  res['Content-Type'] = 'application/json'
  res.body = '{}'
end
Defensive patterns

Strategy: try-catch

Try / catch

begin
  service.get_cert(name)
rescue Puppet::HTTP::ProtocolError => e
  raise unless e.message.include?('No content type')
  # dump headers for diagnosis; likely wrong route or header-stripping proxy
  raise
end

Prevention

When it happens

Trigger: The server (or a proxy in front of it) replies 200 with a body but no Content-Type; a middleware/gateway stripping the header; hitting a plain HTTP port or health-check handler with a puppet path so the reply lacks puppet headers.

Common situations: Custom route handlers on puppetserver that omit content_type; misconfigured nginx/ATS proxying that drops the header; DNS/port mistakes landing on an unrelated web service that answers 200 bare.

Related errors


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