puppetlabs/puppet · error · ArgumentError

'put' requires a string 'body' argument

Error message

'put' requires a string 'body' argument

What it means

Puppet::HTTP::Client#put validates up front that the request body is a String and raises ArgumentError otherwise, before any network I/O. The API intentionally has no implicit serialization — callers must render payloads (JSON, PSON, text) to a string themselves and pair it with a matching Content-Type.

Source

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

    execute_streaming(request, options: options)
  end

  # Submits a PUT HTTP request to the given url
  #
  # @param [URI] url the location to submit the http request
  # @param [String] body the body of the PUT 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
  #
  # @return [Puppet::HTTP::Response] the response
  #
  # @api public
  def put(url, body, headers: {}, params: {}, options: {})
    raise ArgumentError, "'put' requires a string 'body' argument" unless body.is_a?(String)

    url = encode_query(url, params)

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

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

    execute_streaming(request, options: options)
  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

View on GitHub (pinned to e227c27540)

Solutions

  1. Render the payload to a String before the call: body = JSON.generate(data).
  2. For file content, use File.read(path) so you pass the string (and set bytesize-derived length is handled internally).
  3. If the value may legitimately be absent, pass an empty string '' rather than nil.

Example fix

# before
client.put(url, { status: 'done' }, headers: { 'Content-Type' => 'application/json' })

# after
client.put(url, JSON.generate(status: 'done'), headers: { 'Content-Type' => 'application/json' })
Defensive patterns

Strategy: type-guard

Validate before calling

body = JSON.generate(data) unless body.is_a?(String)
client.put(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.put(url, nil, headers: { 'Content-Type' => 'text/plain' }); client.put(url, { 'key' => 'value' }, ...) with a Hash; passing an IO or template object instead of its read content.

Common situations: Migrating from Puppet::Network::HttpPool or RestClient where the library serialized for you; forgetting JSON.generate when sending a report or catalog fragment; reading a file body but passing the File object.

Related errors


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