puppetlabs/puppet · error · Puppet::Util::YamlLoadError

#{path}: #{detail.message}

Error message

#{path}: #{detail.message}

What it means

Puppet::Util::Yaml.safe_load wraps Psych.safe_load; when the document instantiates a class not listed in allowed_classes, Psych raises ::Psych::DisallowedClass and puppet re-raises it as YamlLoadError, prefixing the message with the filename (or (<unknown>)) and keeping the original as cause. Aliases are enabled (aliases: true), so anchors/aliases are fine — the guard is specifically about class tags.

Source

Thrown at lib/puppet/util/yaml.rb:38

  #
  # Attempting to deserialize other classes will raise an YamlLoadError
  # exception unless they are specified in the array of *allowed_classes*.
  # @param [String] yaml The yaml content to parse.
  # @param [Array] allowed_classes Additional list of classes that can be deserialized.
  # @param [String] filename The filename to load from, used if an exception is raised.
  # @raise [YamlLoadException] If deserialization fails.
  # @return The parsed YAML, which can be Hash, Array or scalar types.
  def self.safe_load(yaml, allowed_classes = [], filename = nil)
    if Gem::Version.new(Psych::VERSION) >= Gem::Version.new('3.1.0')
      data = YAML.safe_load(yaml, permitted_classes: allowed_classes, aliases: true, filename: filename)
    else
      data = YAML.safe_load(yaml, allowed_classes, [], true, filename)
    end
    data = false if data.nil?
    data
  rescue ::Psych::DisallowedClass => detail
    path = filename ? "(#{filename})" : "(<unknown>)"
    raise YamlLoadError.new("#{path}: #{detail.message}", detail)
  rescue *YamlLoadExceptions => detail
    raise YamlLoadError.new(detail.message, detail)
  end

  # Safely load the content from a file as YAML.
  #
  # @see Puppet::Util::Yaml.safe_load
  def self.safe_load_file(filename, allowed_classes = [])
    yaml = Puppet::FileSystem.read(filename, :encoding => 'bom|utf-8')
    safe_load(yaml, allowed_classes, filename)
  end

  # Safely load the content from a file as YAML if
  # contents are in valid format. This method does not
  # raise error but returns `nil` when invalid file is
  # given.
  def self.safe_load_file_if_valid(filename, allowed_classes = [])
    safe_load_file(filename, allowed_classes)

View on GitHub (pinned to e227c27540)

Solutions

  1. Pass the classes you actually need: Puppet::Util::Yaml.safe_load_file(path, [Symbol]) (add Date etc. as required — the error names the rejected class)
  2. Regenerate the offending file from plain data: hashes, arrays, strings, integers only
  3. If you control the writer, stop emitting ruby tags — call to_yaml on plain data instead of dumping objects
  4. Use the filename prefix in the message to identify which file is poisoned when several are loaded

Example fix

# before
Puppet::Util::Yaml.safe_load_file('/etc/puppet/extra.yaml')
# YamlLoadError: (/etc/puppet/extra.yaml): Tried to load unspecified class: Symbol

# after
Puppet::Util::Yaml.safe_load_file('/etc/puppet/extra.yaml', [Symbol])
Defensive patterns

Strategy: validation

Validate before calling

text = File.read(path)
if text.match?(/^.*!ruby\//)
  # document contains class tags; decide allowed classes up front
end
Puppet::Util::Yaml.safe_load(text, [Symbol], path)

Try / catch

begin
  Puppet::Util::Yaml.safe_load_file(path, allowed)
rescue Puppet::Util::YamlLoadError => e
  # e.cause is the Psych::DisallowedClass naming the rejected class
  raise "untrusted YAML in #{path}: #{e.message}"
end

Prevention

When it happens

Trigger: Loading YAML containing !ruby/symbol, !ruby/object, !ruby/hash or similar tags while allowed_classes is empty (the default for safe_load_file callers); files written with unsafe Psych.dump on rich objects and later read through safe_load.

Common situations: State/cache files hand-edited or produced by another tool and read by puppet's safe_load_file; Psych 4's safe-by-default behavior surfacing after a Ruby upgrade; YAML exchanged between components where one side serializes rich objects.

Related errors


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