puppetlabs/puppet · error · Puppet::Error

Could not load external node results for %{name}: %{detail}

Error message

Could not load external node results for %{name}: %{detail}

What it means

Catch-all wrapper around the exec node terminus's translate() step: any failure while parsing the ENC script's output with YAML.safe_load, or while normalizing its keys, is re-raised as Puppet::Error with the node name and the underlying detail. It covers malformed YAML (Psych::SyntaxError), disallowed classes (Aliases, unexpected tags) under safe_load, and non-hash-like structures (e.g., a scalar top level making each_with_object raise NoMethodError).

Source

Thrown at lib/puppet/indirector/node/exec.rb:68

    node.fact_merge(facts)
    node
  end

  # Translate the yaml string into Ruby objects.
  def translate(name, output)
    Puppet::Util::Yaml.safe_load(output, [Symbol]).each_with_object({}) do |data, hash|
      case data[0]
      when String
        hash[data[0].intern] = data[1]
      when Symbol
        hash[data[0]] = data[1]
      else
        raise Puppet::Error, _("key is a %{klass}, not a string or symbol") % { klass: data[0].class }
      end
    end
  rescue => detail
    raise Puppet::Error, _("Could not load external node results for %{name}: %{detail}") % { name: name, detail: detail }, detail.backtrace
  end
end

View on GitHub (pinned to e227c27540)

Solutions

  1. Run the ENC manually with a node name and validate the output: /path/to/enc.sh nodename.example.com | ruby -ryaml -e 'YAML.safe_load(STDIN, [Symbol]) or raise'
  2. Send all diagnostics to stderr, never stdout, in the ENC script
  3. Ensure the ENC always prints exactly one YAML hash (even an empty one: '--- {}') on success
  4. If the output uses anchors/aliases, remove them or use YAML.safe_load(..., aliases: true) semantics on the ENC side by expanding aliases before dumping

Example fix

# before (ENC script)
puts "Looking up node..."          # pollutes stdout -> Psych syntax error
puts YAML.dump(result)

# after
warn "Looking up node..."           # stderr only
puts YAML.dump(result)              # stdout is pure YAML
Defensive patterns

Strategy: try-catch

Validate before calling

output = `#{enc_script} #{Shellwords.escape(name)} 2>/dev/null`
require 'yaml'
begin
  YAML.safe_load(output, [Symbol])
rescue Psych::Exception => e
  raise Puppet::Error, "ENC output is not valid YAML: #{e.message}"
end

Try / catch

begin
  node = Puppet::Node.indirection.find(certname)
rescue Puppet::Error => e
  raise unless e.message =~ /Could not load external node results/
  # capture raw ENC output for diagnosis and re-raise with context
  raw = `#{Puppet[:external_nodes]} #{Shellwords.escape(certname)}`
  raise Puppet::Error, "ENC raw output was: #{raw[0,200]}"
end

Prevention

When it happens

Trigger: ENC script prints warnings/deprecation notices before the YAML (making stdout invalid YAML), prints an empty string (safe_load returns nil/false and iteration fails), emits YAML aliases or symbols not whitelisted by safe_load, or crashes and the shell prints a stack trace to stdout which the terminus then tries to parse.

Common situations: ENC scripts that log to stdout instead of stderr; Ruby 3.x Psych defaults rejecting aliases; ENCs that silently die (exit non-zero) with stderr going to logs but stdout empty; scripts emitting multiple YAML documents.

Related errors


AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21). Data as JSON: /api/errors/5805dd7300af659a. Report an issue: GitHub.