puppetlabs/puppet · error · ArgumentError

'post' requires a string 'body' argument

Error message

'post' requires a string 'body' argument

What it means

Puppet::HTTP::Client#post validates that the body argument is a String and raises ArgumentError before any connection is made. Like #put, the API performs no implicit serialization; callers render payloads themselves and must also supply a Content-Type header.

Source

Thrown at lib/puppet/http/client.rb:268

  end

  # Submits a POST HTTP request to the given url
  #
  # @param [URI] url the location to submit the http request
  # @param [String] body the body of the POST request
  # @param [Hash] headers merged with the default headers defined by the client. The
  #   `Content-Type` header is required and should correspond to the type of data passed
  #   as the `body` argument.
  # @param [Hash] params encoded and set as the url query
  # @!macro request_options
  #
  # @yield [Puppet::HTTP::Response] if a block is given yields the response
  #
  # @return [Puppet::HTTP::Response] the response
  #
  # @api public
  def post(url, body, headers: {}, params: {}, options: {}, &block)
    raise ArgumentError, "'post' requires a string 'body' argument" unless body.is_a?(String)

    url = encode_query(url, params)

    request = Net::HTTP::Post.new(url, @default_headers.merge(headers))
    request.body = body
    request.content_length = body.bytesize

    raise ArgumentError, "'post' requires a 'content-type' header" unless request['Content-Type']

    execute_streaming(request, options: options, &block)
  end

  # Submits a DELETE HTTP request to the given url.
  #
  # @param [URI] url the location to submit the http request
  # @param [Hash] headers merged with the default headers defined by the client
  # @param [Hash] params encoded and set as the url query
  # @!macro request_options

View on GitHub (pinned to e227c27540)

Solutions

  1. Serialize first: client.post(url, JSON.generate(data), headers: { 'Content-Type' => 'application/json' }).
  2. Guard optional payloads so nil never reaches the call (default to '').
  3. Check any wrapper gems that forward bodies verbatim from older APIs.

Example fix

# before
client.post(url, { cert: csr.to_pem }, headers: { 'Content-Type' => 'text/plain' })

# after
client.post(url, csr.to_pem, headers: { 'Content-Type' => 'text/plain' })
Defensive patterns

Strategy: type-guard

Validate before calling

body = JSON.generate(data) unless body.is_a?(String)
client.post(url, body, headers: { 'Content-Type' => 'application/json' })

Type guard

string_body = ->(b) { b.is_a?(String) }
raise ArgumentError, 'body must be a String' unless string_body.call(body)

Prevention

When it happens

Trigger: client.post(url, nil, headers: ...); client.post(url, { 'name' => 'x' }, ...) with a Hash; passing a Puppet object (node, report) that older APIs accepted directly.

Common situations: Porting code from Puppet::Network::HttpPool.post or the old REST client where bodies were serialized transparently; forgetting to call .to_s/.to_json on interpolated values; posting a symbol (e.g. :undefined variable).

Related errors


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