puppetlabs/puppet · error · ArgumentError
SSL context must contain a client certificate.
Error message
SSL context must contain a client certificate.
What it means
Raised by Puppet::HTTP::Service::Ca#post_certificate_renewal when the ssl_context passed to the call has no client certificate (ssl_context.client_cert is nil). Certificate renewal is the CA operation where an already-certified agent asks for a new certificate, so the request must authenticate with the existing client cert. Note the check runs after the POST is issued, so a request has already gone on the wire by the time the ArgumentError surfaces.
Source
Thrown at lib/puppet/http/service/ca.rb:127
# Submit a POST request to send a certificate renewal request to the server
#
# @param [Puppet::SSL::SSLContext] ssl_context
#
# @return [Array<Puppet::HTTP::Response, String>] The request response
#
# @api public
def post_certificate_renewal(ssl_context)
headers = add_puppet_headers(HEADERS)
headers['Content-Type'] = 'text/plain'
response = @client.post(
with_base_url('/certificate_renewal'),
'', # Puppet::HTTP::Client.post requires a body, the API endpoint does not
headers: headers,
options: { ssl_context: ssl_context }
)
raise ArgumentError, _('SSL context must contain a client certificate.') unless ssl_context.client_cert
process_response(response)
[response, response.body.to_s]
end
end
View on GitHub (pinned to e227c27540)
Solutions
- Load the context for the full agent chain from the SSL directory (Puppet::SSL::Provider#load_context) so client_cert is populated, and only call renewal on nodes that already hold a signed cert
- If the node has no certificate yet, switch to CSR submission (put_certificate_request) instead of renewal
- When building a context manually, pass client_cert: and private_key: to create_context
- Verify cert.pem and private_key.pem exist under the agent's ssl directory before invoking the CA service
Example fix
# before (ruby) ssl_context = provider.create_context(cacerts: [ca_cert], crls: [crl]) client.post_certificate_renewal(ssl_context) # => ArgumentError # after ssl_context = provider.create_context( cacerts: [ca_cert], crls: [crl], client_cert: agent_cert, private_key: agent_key ) client.post_certificate_renewal(ssl_context)
Defensive patterns
Strategy: validation
Validate before calling
# ruby raise ArgumentError, 'ssl_context has no client certificate' if ssl_context.client_cert.nil? client.post_certificate_renewal(ssl_context)
Type guard
def has_client_cert?(ctx) ctx.respond_to?(:client_cert) && !ctx.client_cert.nil? end
Try / catch
begin
client.post_certificate_renewal(ssl_context)
rescue ArgumentError => e
raise unless e.message.include?('client certificate')
client.put_certificate_request(certname, csr) # fall back to enrollment
end Prevention
- Obtain request contexts via Puppet::SSL::Provider#load_context rather than hand-building them
- Check ssl_context.client_cert before any CA call that authenticates the agent
- Gate renewal flows on the existence of $ssldir/certs/<certname>.pem before invoking them
When it happens
Trigger: Calling client.post_certificate_renewal(ssl_context) with a context built without a client cert — e.g. Puppet::SSL::Provider#create_context called with only :cacerts/:crls and no :client_cert/:private_key, or a CA-chain-only context instead of one for the full agent chain.
Common situations: Running certificate renewal on a node that never obtained a signed certificate (renewal used where submit_certificate_request is the right enrollment path); custom tooling that hand-builds an SSLContext and forgets the client cert pair; the agent's cert files were cleaned (puppet certificate clean / ssl dir wiped) but renewal logic still runs.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- The client certificate is missing from '%{path}'
- PathPatterns cannot be created with a zero byte.
- Request to Puppet Forge failed. Detail: %{detail}.
- puppet.tasks/unparseable-metadata
- An action must be specified.
AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21).
Data as JSON: /api/errors/d5822111f0ec2471.
Report an issue: GitHub.