puppetlabs/puppet · error · ArgumentError
'put' requires a 'content-type' header
Error message
'put' requires a 'content-type' header
What it means
Puppet::HTTP::Client#put requires a Content-Type header on every request: the client's default headers do not include one, so if the caller's headers hash doesn't supply it (with that exact case), an ArgumentError is raised after the body is set but before the request is sent.
Source
Thrown at lib/puppet/http/client.rb:247
# @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
# 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
#View on GitHub (pinned to e227c27540)
Solutions
- Add the header with exact casing: headers: { 'Content-Type' => 'application/json' }.
- Match the type to the body's serialization (application/json for JSON, text/plain for plain text, application/x-www-form-urlencoded for form bodies).
- Prefer the high-level Puppet::HTTP::Service APIs, which set Content-Type for you.
Example fix
# before
client.put(url, JSON.generate(data), headers: {})
# after
client.put(url, JSON.generate(data), headers: { 'Content-Type' => 'application/json' }) Defensive patterns
Strategy: validation
Validate before calling
headers = { 'Content-Type' => 'application/json' }.merge(headers)
client.put(url, body, headers: headers) Type guard
has_content_type = ->(h) { h.any? { |k, _| k.is_a?(String) && k == 'Content-Type' } }
raise ArgumentError, 'Content-Type required' unless has_content_type.call(headers) Prevention
- Build headers through one helper that always sets Content-Type with exact casing.
- Match Content-Type to how the body was serialized.
When it happens
Trigger: client.put(url, 'text') with headers: {} ; passing the header lowercase as 'content-type' — Net::HTTP header lookup in the guard is case-sensitive, so request['Content-Type'] stays nil even though Net::HTTP would send it.
Common situations: Sending JSON without 'application/json' because the old HttpPool API added it; header-name casing copied from a different HTTP library; proxy code forwarding only some headers.
Related errors
- 'post' requires a 'content-type' header
- 'put' requires a string 'body' argument
- Location response header is missing
- Failed to parse Retry-After header '%{retry_after}' as an in
- No content type in http response; cannot parse
AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21).
Data as JSON: /api/errors/539fd5b377c22ff6.
Report an issue: GitHub.