puppetlabs/puppet · error · Puppet::DevError

Could not convert change %{name} to string: %{detail}

Error message

Could not convert change %{name} to string: %{detail}

What it means

Property::Ensure#change_to_s formats 'created', 'removed' or '<name> changed <is> to <should>' for the ensure property. If is_to_s or should_to_s raise while formatting, the error is re-raised as Puppet::DevError with the property name and the underlying detail - the same pattern as Property#change_to_s, but without logging the cause first. Custom ensure enumerations (mounted, purged, latest...) are the usual source.

Source

Thrown at lib/puppet/property/ensure.rb:65

  def self.inherited(sub)
    # Add in the two properties that everyone will have.
    sub.class_eval do
    end
  end

  def change_to_s(currentvalue, newvalue)
    if currentvalue == :absent || currentvalue.nil?
      _("created")
    elsif newvalue == :absent
      _("removed")
    else
      _('%{name} changed %{is} to %{should}') % { name: name, is: is_to_s(currentvalue), should: should_to_s(newvalue) }
    end
  rescue Puppet::Error
    raise
  rescue => detail
    raise Puppet::DevError, _("Could not convert change %{name} to string: %{detail}") % { name: name, detail: detail }, detail.backtrace
  end

  # Retrieves the _is_ value for the ensure property.
  # The existence of the resource is checked by first consulting the provider (if it responds to
  # `:exists`), and secondly the resource. A a value of `:present` or `:absent` is returned
  # depending on if the managed entity exists or not.
  #
  # @return [Symbol] a value of `:present` or `:absent` depending on if it exists or not
  # @raise [Puppet::DevError] if neither the provider nor the resource responds to `:exists`
  #
  def retrieve
    # XXX This is a problem -- whether the object exists or not often
    # depends on the results of other properties, yet we're the first property
    # to get checked, which means that those other properties do not have
    # @is values set.  This seems to be the source of quite a few bugs,
    # although they're mostly logging bugs, not functional ones.
    prov = @resource.provider
    if prov && prov.respond_to?(:exists?)

View on GitHub (pinned to e227c27540)

Solutions

  1. Fix the helper named in the detail (munge, is_to_s or should_to_s) so it handles the failing value.
  2. Coerce defensively: fall back to inspect for values of unknown shape.
  3. Re-raise Puppet::Error subclasses from helpers so they are not wrapped.

Example fix

# before (custom type ensure)
def should_to_s(v)
  v.split('=').last    # NoMethodError for symbols -> DevError wrapper
end

# after
def should_to_s(v)
  v.respond_to?(:split) ? v.split('=').last.to_s : v.inspect
end
Defensive patterns

Strategy: try-catch

Try / catch

msg = begin
  ensure_prop.change_to_s(is_value, should_value)
rescue Puppet::DevError
  "ensure #{is_value.inspect} -> #{should_value.inspect}"   # fallback text
end

Prevention

When it happens

Trigger: A custom type's ensure property supports values whose munge or *_to_s helper raises; reporting tooling calls change_to_s with values that never occur during normal syncing; should_to_s dereferences provider state while no provider is present.

Common situations: Custom ensure values with sloppy munges; report parsers or tools that render change messages for arbitrary catalogs.

Related errors


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