{"record":{"id":"53b36716df8240e5","repo":"puppetlabs/puppet","slug":"could-not-retrieve-local-facts-detail","errorCode":null,"errorMessage":"Could not retrieve local facts: %{detail}","messagePattern":"Could not retrieve local facts: %(.+?)","errorType":"exception","errorClass":"Puppet::Error","httpStatus":null,"severity":"error","filePath":"lib/puppet/configurer/fact_handler.rb","lineNumber":28,"sourceCode":"# easier to test.\nmodule Puppet::Configurer::FactHandler\n  def find_facts\n    # This works because puppet agent configures Facts to use 'facter' for\n    # finding facts and the 'rest' terminus for caching them.  Thus, we'll\n    # compile them and then \"cache\" them on the server.\n\n    facts = Puppet::Node::Facts.indirection.find(Puppet[:node_name_value], :environment => Puppet::Node::Environment.remote(@environment))\n    unless Puppet[:node_name_fact].empty?\n      Puppet[:node_name_value] = facts.values[Puppet[:node_name_fact]]\n      facts.name = Puppet[:node_name_value]\n    end\n    facts\n  rescue SystemExit, NoMemoryError\n    raise\n  rescue Exception => detail\n    message = _(\"Could not retrieve local facts: %{detail}\") % { detail: detail }\n    Puppet.log_exception(detail, message)\n    raise Puppet::Error, message, detail.backtrace\n  end\n\n  def facts_for_uploading\n    encode_facts(find_facts)\n  end\n\n  def encode_facts(facts)\n    # facts = find_facts\n\n    # NOTE: :facts specified as parameters are URI encoded here,\n    # then  encoded for a second time depending on their length:\n    #\n    # <= 1024 characters sent via query string of a HTTP GET, additionally query string encoded\n    # > 1024 characters sent in POST data, additionally x-www-form-urlencoded\n    # so it's only important that encoding method here return original values\n    # correctly when CGI.unescape called against it (in compiler code)\n    if Puppet[:preferred_serialization_format] == \"pson\"\n      { :facts_format => :pson, :facts => Puppet::Util.uri_query_encode(facts.render(:pson)) }","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/configurer/fact_handler.rb#L10-L46","documentation":"Wrapping error from `Puppet::Configurer::FactHandler#find_facts`: collecting local facts via `Puppet::Node::Facts.indirection.find` raised an unexpected Exception (SystemExit and NoMemoryError are explicitly re-raised untouched). The original exception is logged through `Puppet.log_exception` and re-raised as Puppet::Error with the original backtrace attached, so `%{detail}` is the text of the underlying Facter failure.","triggerScenarios":"Any Ruby exception while Facter resolves facts on the agent: a custom fact whose setcode block raises (NoMethodError, Errno::*), an external fact script that fails, a Facter/Puppet version mismatch breaking the fact API, or a broken PATH preventing helper execution.","commonSituations":"Custom facts written for legacy Facter using removed APIs; external facts with a bad shebang or non-zero exit; hybrid Facter 3/4 installs after an agent upgrade; module-dropped facts using platform-specific code that fails on a new OS.","solutions":["Reproduce outside Puppet: run `facter -p` (custom facts) and `facter` — the same failure surfaces with its backtrace","Fix or harden the failing custom/external fact (rescue inside setcode, check File.exist? before reads)","Verify version compatibility (e.g. Puppet 7 expects Facter 4.x) and consistent gem/native installs","Run `puppet agent -t --trace`: the wrapped original backtrace shows exactly which fact failed"],"exampleFix":"# before: custom fact crashes when file is absent\nFacter.add(:app_version) do\n  setcode { File.read('/etc/app/version').strip }  # Errno::ENOENT\nend\n# after\nFacter.add(:app_version) do\n  setcode do\n    File.exist?('/etc/app/version') ? File.read('/etc/app/version').strip : nil\n  end\nend","handlingStrategy":"try-catch","validationCode":"# smoke-test fact resolution before an agent run\nfacts = Facter.to_hash\nabort 'fact resolution failed' if facts.nil? || facts.empty?","typeGuard":null,"tryCatchPattern":"begin\n  facts = Puppet::Node::Facts.indirection.find(Puppet[:node_name_value], environment: env)\nrescue Puppet::Error => e\n  # e.message is 'Could not retrieve local facts: <original>'\n  # the original backtrace is attached; surface it to locate the bad fact\n  $stderr.puts e.message\n  warn 'run `facter -p --debug` to identify the failing custom fact'\n  raise\nend","preventionTips":["Wrap custom fact setcode blocks in begin/rescue and return nil on failure","Test new facts with `facter -p <name>` before shipping them to agents","Keep Puppet and Facter major versions compatible (e.g. Puppet 7 ↔ Facter 4)"],"tags":["facter","facts","custom-facts","puppet-agent"],"backgroundTag":"fact-retrieval-failure","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}