puppetlabs/puppet · error · ArgumentError
The 'persistence' hash is missing the keys: #{missing.join('
Error message
The 'persistence' hash is missing the keys: #{missing.join(', ')} What it means
Puppet::HTTP::Service::Compiler#post_catalog4 (the v4 catalog API) requires a persistence: keyword that must be a Hash containing at least :facts and :catalog keys, which tell the server which parts of the request to persist. ArgumentError is raised when persistence is not a Hash at all, or when either required key is absent (the message lists the missing keys).
Source
Thrown at lib/puppet/http/service/compiler.rb:166
# @param [String] transaction_uuid The id for tracking the catalog compilation and
# report submission.
# @param [String] job_id The id of the orchestrator job that triggered this run.
# @param [Hash] options A hash of options beyond direct input to catalogs. Options:
# - prefer_requested_environment Whether to always override a node's classified
# environment with the one supplied in the request. If this is true and no environment
# is supplied, fall back to the classified environment, or finally, 'production'.
# - capture_logs Whether to return the errors and warnings that occurred during
# compilation alongside the catalog in the response body.
# - log_level The logging level to use during the compile when capture_logs is true.
# Options are 'err', 'warning', 'info', and 'debug'.
#
# @return [Array<Puppet::HTTP::Response, Puppet::Resource::Catalog, Array<String>>] An array
# containing the request response, the deserialized catalog returned by
# the server and array containing logs (log array will be empty if capture_logs is false)
#
def post_catalog4(certname, persistence:, environment:, facts: nil, trusted_facts: nil, transaction_uuid: nil, job_id: nil, options: nil)
unless persistence.is_a?(Hash) && (missing = [:facts, :catalog] - persistence.keys.map(&:to_sym)).empty?
raise ArgumentError, "The 'persistence' hash is missing the keys: #{missing.join(', ')}"
end
raise ArgumentError, "Facts must be a Hash not a #{facts.class}" unless facts.nil? || facts.is_a?(Hash)
body = {
certname: certname,
persistence: persistence,
environment: environment,
transaction_uuid: transaction_uuid,
job_id: job_id,
options: options
}
body[:facts] = { values: facts } unless facts.nil?
body[:trusted_facts] = { values: trusted_facts } unless trusted_facts.nil?
headers = add_puppet_headers(
'Accept' => get_mime_types(Puppet::Resource::Catalog).join(', '),
'Content-Type' => 'application/json'
)
View on GitHub (pinned to e227c27540)
Solutions
- Pass persistence: {facts: true, catalog: true} (or the appropriate booleans) so both required keys are present
- Symbolize keys before the call: persistence.transform_keys(&:to_sym) when the hash comes from JSON or config
- Fail fast in your own wrapper: reject the call unless persistence.is_a?(Hash) and both keys are present
Example fix
# before (ruby)
api.post_catalog4(certname,
persistence: { 'facts' => true },
environment: 'production')
# => ArgumentError: missing keys: catalog
# after
api.post_catalog4(certname,
persistence: { facts: true, catalog: true },
environment: 'production') Defensive patterns
Strategy: validation
Validate before calling
# ruby
missing = %i[facts catalog] - persistence.keys.map(&:to_sym)
raise ArgumentError, "persistence missing: #{missing.join(', ')}" unless persistence.is_a?(Hash) && missing.empty?
api.post_catalog4(certname, persistence: persistence, environment: env) Type guard
def valid_persistence?(h) h.is_a?(Hash) && (%i[facts catalog] - h.keys.map(&:to_sym)).empty? end
Try / catch
begin
api.post_catalog4(certname, persistence: persistence, environment: env)
rescue ArgumentError => e
raise unless e.message.include?('persistence')
persistence = { facts: true, catalog: true }
retry
end Prevention
- Construct the persistence hash inline with both keys visible: {facts: true, catalog: true}
- Normalize incoming config with transform_keys(&:to_sym) before the call
- Unit-test post_catalog4 argument handling so signature drift is caught in CI
When it happens
Trigger: Calling api.post_catalog4(certname, persistence: {facts: true}, environment: env) with :catalog missing; passing persistence: nil or a String; passing keys that do not symbolize to :facts/:catalog.
Common situations: Code written from memory or against an older draft of the v4 API signature; copy-pasting the options hash from post_catalog (v3) calls, which has no persistence argument; tests that build the hash dynamically and skip a key under a conditional.
Understand the failure class
Background: Missing required parameter errors: what 'X is required' and 'the required X param is missing' mean, and how to fix them — this error's family across 27 libraries.
Related errors
- Facts must be a Hash not a #{facts.class}
- Unknown service #{name}
- Extra arguments detected: %{args} Did you mean to run: pup
- Please provide a file or checksum to diff with
- Failed to diff files
AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21).
Data as JSON: /api/errors/28724be28fbaf7cf.
Report an issue: GitHub.