puppetlabs/puppet · error · Puppet::Error
Could not destroy %{json} %{request}: %{detail}
Error message
Could not destroy %{json} %{request}: %{detail} What it means
The JSON terminus destroy calls Puppet::FileSystem.unlink on the stored file. Errno::ENOENT is swallowed and 1 is returned to emulate success, but any other error - EACCES/EPERM, EISDIR, read-only filesystem, EROFS - is re-raised as Puppet::Error 'Could not destroy json <key>: <original message>' with the original backtrace preserved.
Source
Thrown at lib/puppet/indirector/json.rb:28
class Puppet::Indirector::JSON < Puppet::Indirector::Terminus
def find(request)
load_json_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_json(request.instance).force_encoding(Encoding::BINARY) }
rescue TypeError => detail
Puppet.log_exception(detail, _("Could not save %{json} %{request}: %{detail}") % { json: 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 %{json} %{request}: %{detail}") % { json: 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_json_from_file(file, request.key)
end
end
# Return the path to a given node's file.
def path(name, ext = '.json')
if name =~ Puppet::Indirector::BadNameRegexp then
Puppet.crit(_("directory traversal detected in %{json}: %{name}") % { json: self.class, name: name.inspect })
raise ArgumentError, _("invalid key")
end
View on GitHub (pinned to e227c27540)
Solutions
- Read the <detail> suffix; it names the real errno (permission denied, read-only file system, ...).
- Fix ownership: chown -R <serviceuser>:<servicegroup> "$(puppet config print server_datadir)" (and client_datadir on agents where relevant).
- Remount writable or relocate the datadir if the filesystem is read-only.
- If SELinux is enforcing, check audit logs and run restorecon on the datadir.
- Remove a stray directory-at-path manually if a path-type clash caused the error.
Example fix
# before: service user cannot unlink root-owned store file # Could not destroy json node.example.com: Permission denied @ unlink_internal # after chown -R puppet:puppet /opt/puppetlabs/server/data/puppet systemctl restart puppetserver
Defensive patterns
Strategy: try-catch
Validate before calling
path = File.join(store_dir, indirection_name, key + '.json') 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
end Prevention
- Run the puppet service under one user; never mix root and service-user writes to datadirs.
- Declare datadir ownership in provisioning code.
- Exercise destroy paths in smoke tests so permission drift surfaces early.
When it happens
Trigger: Destroying an entry in a json-backed indirection store when server_datadir (or client_datadir) files are owned by root while the service runs as puppet; the datadir lives on a read-only mount; the path points at a directory instead of a file.
Common situations: Master data dirs chowned incorrectly after package upgrades; runs mixed between root and the service user; SELinux denials; containerized puppetservers with read-only persistent volumes.
Related errors
- Could not read JSON data for %{name} %{key}: %{detail}
- Could not destroy %{name} %{request}: %{detail}
- Could not back up %{file}: %{detail}
- %{path} is not readable
- invalid key
AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21).
Data as JSON: /api/errors/0919bbc3bcc3da33.
Report an issue: GitHub.