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

The request body is invalid: %{message}

Error message

The request body is invalid: %{message}

What it means

For PUT/POST requests the handler deserializes the body with model_class.convert_from using the format derived from the Content-Type header. Any exception raised during that conversion is wrapped into HTTP 400 'The request body is invalid: <original message>'. The underlying parser message is preserved, so the actual cause (bad JSON, wrong schema, truncated body) is visible in the response.

Source

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

  def accepted_response_formatters_for(model_class, request)
    request.response_formatters_for(model_class.supported_formats)
  end

  # Return the first response formatter that the client accepts and
  # the server supports, or default to 'application/json'.
  def accepted_response_formatter_or_json_for(model_class, request)
    request.response_formatters_for(model_class.supported_formats, "application/json").first
  end

  def read_body_into_model(model_class, request)
    data = request.body.to_s
    formatter = request.formatter

    if formatter.supported?(model_class)
      begin
        return model_class.convert_from(formatter.name.to_s, data)
      rescue => e
        raise Puppet::Network::HTTP::Error::HTTPBadRequestError, _("The request body is invalid: %{message}") % { message: e.message }
      end
    end

    # TRANSLATORS "mime-type" is a keyword and should not be translated
    raise Puppet::Network::HTTP::Error::HTTPUnsupportedMediaTypeError.new(
      _("Client sent a mime-type (%{header}) that doesn't correspond to a format we support") % { header: request.headers['content-type'] },
      Puppet::Network::HTTP::Issues::UNSUPPORTED_MEDIA_TYPE
    )
  end

  def indirection_method(http_method, indirection)
    raise Puppet::Network::HTTP::Error::HTTPMethodNotAllowedError, _("No support for http method %{http_method}") % { http_method: http_method } unless METHOD_MAP[http_method]

    method = METHOD_MAP[http_method][plurality(indirection)]
    unless method
      raise Puppet::Network::HTTP::Error::HTTPBadRequestError, _("No support for plurality %{indirection} for %{http_method} operations") % { indirection: plurality(indirection), http_method: http_method }
    end

View on GitHub (pinned to e227c27540)

Solutions

  1. Capture the exact body sent and validate it locally (JSON.parse or the format's convert_from) to locate the malformed part
  2. Ensure the Content-Type header matches the actual body format (application/json for JSON)
  3. Align agent and server versions, or adjust the payload to the schema the server expects
  4. Check body size limits in intermediate proxies if bodies arrive truncated

Example fix

# before: submitting invalid JSON
curl -X PUT -H 'Content-Type: application/json' \
  -d '{ "name": "mynode", ' \
  "https://puppet:8140/puppet/v3/report/mynode?environment=production"

# after: valid, complete JSON document sent from a file
curl -X PUT -H 'Content-Type: application/json' \
  -d @report.json \
  "https://puppet:8140/puppet/v3/report/mynode?environment=production"
Defensive patterns

Strategy: try-catch

Validate before calling

require 'json'

payload = report.to_json
JSON.parse(payload)  # raises now, with our own context, instead of server-side
client.put("/puppet/v3/report/#{certname}", body: payload,
           headers: { 'Content-Type' => 'application/json' },
           params: { environment: env })

Try / catch

begin
  client.put("/puppet/v3/report/#{certname}",
             body: payload,
             headers: { 'Content-Type' => 'application/json' },
             params: { environment: env })
rescue Puppet::Network::HTTP::Error::HTTPBadRequestError => e
  # e.message carries the underlying parse error, e.g. 'The request body is invalid: unexpected token'
  raise "report rejected for #{certname}: #{e.message}"
end

Prevention

When it happens

Trigger: PUT /puppet/v3/report/mynode with malformed JSON; submitting facts whose payload does not match the expected model schema; a Content-Type header claiming a format the body is not; truncated bodies from proxy timeouts; agent/server version skew where the payload schema differs.

Common situations: Custom report or fact submitters that hand-roll JSON; agent and server versions out of step so new fields are rejected; load balancers or proxies truncating large catalog/report bodies.

Related errors


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