{"record":{"id":"b5f1ec11eb14efe8","repo":"puppetlabs/puppet","slug":"facts-but-no-fact-format-provided-for-request","errorCode":null,"errorMessage":"Facts but no fact format provided for %{request}","messagePattern":"Facts but no fact format provided for %(.+?)","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/puppet/indirector/catalog/compiler.rb","lineNumber":30,"sourceCode":"\nclass Puppet::Resource::Catalog::Compiler < Puppet::Indirector::Code\n  desc \"Compiles catalogs on demand using Puppet's compiler.\"\n\n  include Puppet::Util\n  include Puppet::Util::Checksums\n\n  attr_accessor :code\n\n  # @param request [Puppet::Indirector::Request] an indirection request\n  #   (possibly) containing facts\n  # @return [Puppet::Node::Facts] facts object corresponding to facts in request\n  def extract_facts_from_request(request)\n    text_facts = request.options[:facts]\n    return unless text_facts\n\n    format = request.options[:facts_format]\n    unless format\n      raise ArgumentError, _(\"Facts but no fact format provided for %{request}\") % { request: request.key }\n    end\n\n    Puppet::Util::Profiler.profile(_(\"Found facts\"), [:compiler, :find_facts]) do\n      facts = text_facts.is_a?(Puppet::Node::Facts) ? text_facts :\n                                                      convert_wire_facts(text_facts, format)\n\n      unless facts.name == request.key\n        raise Puppet::Error, _(\"Catalog for %{request} was requested with fact definition for the wrong node (%{fact_name}).\") % { request: request.key.inspect, fact_name: facts.name.inspect }\n      end\n\n      return facts\n    end\n  end\n\n  def save_facts_from_request(facts, request)\n    Puppet::Node::Facts.indirection.save(facts, nil,\n                                         :environment => request.environment,\n                                         :transaction_uuid => request.options[:transaction_uuid])","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/indirector/catalog/compiler.rb#L12-L48","documentation":"In the catalog compiler terminus, extract_facts_from_request pulls client facts out of an indirector request. If request.options[:facts] is present but request.options[:facts_format] is nil, the server cannot know how to deserialize the payload and raises ArgumentError. Requests without facts return early and are unaffected.","triggerScenarios":"Calling Puppet::Resource::Catalog.indirection.find(key, facts: serialized, ...) without facts_format; building a Puppet::Indirector::Request by hand for testing or tooling and setting :facts but not :facts_format; a custom face or terminus that forwards only one of the two options.","commonSituations":"Legacy tooling ported from an era when PSON was assumed and the format option was optional; custom orchestration code (puppet-load style harnesses) driving the catalog indirection directly; request-munging middleware that drops the facts_format option.","solutions":["Always set both options together: facts: serialized_facts, facts_format: 'application/json' (or 'pson' for old agents)","Centralize request construction in a helper that enforces the pairing rather than building options hashes ad hoc","When facts are not needed, omit :facts entirely so the early return path is taken"],"exampleFix":"# before (ruby)\nPuppet::Resource::Catalog.indirection.find(\n  'node1.example.com',\n  facts: facts_json)  # => ArgumentError: Facts but no fact format provided\n\n# after\nPuppet::Resource::Catalog.indirection.find(\n  'node1.example.com',\n  facts: facts_json,\n  facts_format: 'application/json')","handlingStrategy":"validation","validationCode":"# ruby\nopts = {}\nif serialized_facts\n  opts[:facts] = serialized_facts\n  opts[:facts_format] = 'application/json'  # never one without the other\nend\nPuppet::Resource::Catalog.indirection.find(name, opts)","typeGuard":"def facts_options_complete?(opts)\n  opts[:facts].nil? || !opts[:facts_format].nil?\nend","tryCatchPattern":"begin\n  Puppet::Resource::Catalog.indirection.find(name, opts)\nrescue ArgumentError => e\n  raise unless e.message.include?('fact format')\n  opts = opts.merge(facts_format: 'application/json')\n  retry\nend","preventionTips":["Wrap indirector request construction in one helper that pairs :facts with :facts_format","Omit :facts entirely when the compile does not need facts","Spec-test the request builder so the pairing cannot regress"],"tags":["puppet-compiler","facts","indirector","argument-validation"],"backgroundTag":"missing-required-parameter","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}