puppetlabs/puppet · error · Puppet::Error

Could not destroy %{name} %{request}: %{detail}

Error message

Could not destroy %{name} %{request}: %{detail}

What it means

The MessagePack terminus destroy calls Puppet::FileSystem.unlink on the stored .msgpack file. Errno::ENOENT is swallowed (returns 1, emulating success), but any other error - EACCES/EPERM, EISDIR, read-only filesystem - is re-raised as Puppet::Error 'Could not destroy msgpack <key>: <original message>' with the original backtrace.

Source

Thrown at lib/puppet/indirector/msgpack.rb:36

  def find(request)
    load_msgpack_from_file(path(request.key), request.key)
  end

  def save(request)
    filename = path(request.key)
    FileUtils.mkdir_p(File.dirname(filename))

    Puppet::FileSystem.replace_file(filename, 0o660) { |f| f.print to_msgpack(request.instance) }
  rescue TypeError => detail
    Puppet.log_exception(detail, _("Could not save %{name} %{request}: %{detail}") % { name: name, request: request.key, detail: detail })
  end

  def destroy(request)
    Puppet::FileSystem.unlink(path(request.key))
  rescue => detail
    unless detail.is_a? Errno::ENOENT
      raise Puppet::Error, _("Could not destroy %{name} %{request}: %{detail}") % { name: name, request: request.key, detail: detail }, detail.backtrace
    end

    1 # emulate success...
  end

  def search(request)
    Dir.glob(path(request.key)).collect do |file|
      load_msgpack_from_file(file, request.key)
    end
  end

  # Return the path to a given node's file.
  def path(name, ext = '.msgpack')
    if name =~ Puppet::Indirector::BadNameRegexp then
      Puppet.crit(_("directory traversal detected in %{indirection}: %{name}") % { indirection: self.class, name: name.inspect })
      raise ArgumentError, _("invalid key")
    end

View on GitHub (pinned to e227c27540)

Solutions

  1. Read the <detail> suffix; it identifies the real errno.
  2. chown -R <serviceuser>:<servicegroup> the datadir (Puppet[:server_datadir] / Puppet[:client_datadir]) so the service user can unlink.
  3. Remount writable or move the datadir off the read-only filesystem.
  4. Check SELinux audit logs and restorecon when labels are the cause.

Example fix

# before
# Could not destroy msgpack node1.example.com: Read-only file system @ unlink_internal

# after: relocate the datadir to a writable volume
# puppet.conf: [server] server_datadir = /var/lib/puppet/server_data
systemctl restart puppetserver
Defensive patterns

Strategy: try-catch

Validate before calling

path = File.join(store_dir, indirection_name, key + '.msgpack')
raise ArgumentError, 'not removable by this user' unless File.writable?(File.dirname(path)) && (File.writable?(path) || !File.exist?(path))

Try / catch

begin
  indirection.destroy(key)
rescue Puppet::Error => e
  Puppet.err('destroy failed: ' + e.message) # operator follow-up: fix ownership/mount
end

Prevention

When it happens

Trigger: Destroying an entry in a msgpack-backed indirection store when the datadir file is owned by another user, the filesystem is read-only, or the path is a directory.

Common situations: Service/datadir ownership drift after upgrades or container rebuilds; mixed root/service-user runs; read-only persistent volumes; SELinux label changes on migrated volumes.

Related errors


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