{"record":{"id":"3df3ce6dae7756f8","repo":"puppetlabs/puppet","slug":"find-uri-resulted-in-404-with-the-message-bo-3df3ce","errorCode":null,"errorMessage":"Find %{uri} resulted in 404 with the message: %{body}","messagePattern":"Find %(.+?) resulted in 404 with the message: %(.+?)","errorType":"http","errorClass":"Puppet::Error","httpStatus":404,"severity":"error","filePath":"lib/puppet/indirector/facts/rest.rb","lineNumber":23,"sourceCode":"\nclass Puppet::Node::Facts::Rest < Puppet::Indirector::REST\n  desc \"Find and save facts about nodes over HTTP via REST.\"\n\n  def find(request)\n    session = Puppet.lookup(:http_session)\n    api = session.route_to(:puppet)\n    _, facts = api.get_facts(\n      request.key,\n      environment: request.environment.to_s\n    )\n    facts\n  rescue Puppet::HTTP::ResponseError => e\n    if e.response.code == 404\n      return nil unless request.options[:fail_on_404]\n\n      _, body = parse_response(e.response)\n      msg = _(\"Find %{uri} resulted in 404 with the message: %{body}\") % { uri: elide(e.response.url.path, 100), body: body }\n      raise Puppet::Error, msg\n    else\n      raise convert_to_http_error(e.response)\n    end\n  end\n\n  def save(request)\n    raise ArgumentError, _(\"PUT does not accept options\") unless request.options.empty?\n\n    session = Puppet.lookup(:http_session)\n    api = session.route_to(:puppet)\n    api.put_facts(\n      request.key,\n      facts: request.instance,\n      environment: request.environment.to_s\n    )\n\n    # preserve existing behavior\n    nil","sourceCodeStart":5,"sourceCodeEnd":41,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/indirector/facts/rest.rb#L5-L41","documentation":"The facts REST terminus maps a server 404 on a facts GET to nil (fact set not found) unless the request carried options[:fail_on_404], in which case it raises Puppet::Error embedding the request path and the server's response body. Same contract as the catalog REST terminus: find returns nil for missing data unless the caller explicitly demands failure.","triggerScenarios":"Puppet::Node::Facts.indirection.find(name, fail_on_404: true) against a server that answers 404 — facts were never stored, were cleaned, or the request hit a server without that node's facts.","commonSituations":"Inventory scripts probing facts for decommissioned or never-registered nodes; facts cleaned on the master or purged from PuppetDB; a load balancer routing the GET to a server whose fact store does not contain the node.","solutions":["Drop fail_on_404 and treat nil as 'no facts on this server' for discovery or inventory use cases","If facts should exist, verify server-side (puppet facts find or a PuppetDB query) and re-submit by running the agent on the node","Check the response body in the message — it distinguishes unknown-node 404s from unknown-route 404s"],"exampleFix":"# before (ruby)\nfacts = Puppet::Node::Facts.indirection.find(name,\n  fail_on_404: true)  # 404 => Puppet::Error\n\n# after\nfacts = Puppet::Node::Facts.indirection.find(name)  # 404 => nil\nputs 'no facts stored' unless facts","handlingStrategy":"try-catch","validationCode":"# ruby\nfacts = Puppet::Node::Facts.indirection.find(name)  # default nil semantics\nreturn :no_facts if facts.nil?","typeGuard":"def facts_stored?(name)\n  !Puppet::Node::Facts.indirection.find(name).nil?\nend","tryCatchPattern":"begin\n  Puppet::Node::Facts.indirection.find(name, fail_on_404: true)\nrescue Puppet::Error => e\n  raise unless e.message.include?('404')\n  nil  # treat as absent and report\nend","preventionTips":["Use fail_on_404 only where a missing fact set must abort the workflow","Distinguish 404 body types (unknown node vs unknown route) before escalating","For fleet inventory, query PuppetDB instead of the facts indirection"],"tags":["puppet-rest","facts","http-404","indirector"],"backgroundTag":"http-404-resource-not-found","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}