{"record":{"id":"494b6fb1405ab8e0","repo":"puppetlabs/puppet","slug":"klass-could-not-write-path-detail","errorCode":null,"errorMessage":"%{klass} could not write %{path}: %{detail}","messagePattern":"%(.+?) could not write %(.+?): %(.+?)","errorType":"exception","errorClass":"Puppet::Error","httpStatus":null,"severity":"error","filePath":"lib/puppet/util/filetype.rb","lineNumber":66,"sourceCode":"        raise\n      rescue => detail\n        message = _(\"%{klass} could not read %{path}: %{detail}\") % { klass: self.class, path: @path, detail: detail }\n        Puppet.log_exception(detail, message)\n        raise Puppet::Error, message, detail.backtrace\n      end\n\n      # And then the write method\n      define_method(:real_write, instance_method(:write))\n      define_method(:write) do |text|\n        val = real_write(text)\n        @synced = Time.now\n        val\n      rescue Puppet::Error\n        raise\n      rescue => detail\n        message = _(\"%{klass} could not write %{path}: %{detail}\") % { klass: self.class, path: @path, detail: detail }\n        Puppet.log_exception(detail, message)\n        raise Puppet::Error, message, detail.backtrace\n      end\n    end\n  end\n\n  def self.filetype(type)\n    @filetypes[type]\n  end\n\n  # Pick or create a filebucket to use.\n  def bucket\n    @bucket ||= Puppet::Type.type(:filebucket).mkdefaultbucket.bucket\n  end\n\n  def initialize(path, default_mode = nil)\n    raise ArgumentError, _(\"Path is nil\") if path.nil?\n\n    @path = path\n    @default_mode = default_mode","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/util/filetype.rb#L48-L84","documentation":"The write twin of FileType's read decorator: exceptions from the filetype-specific write that are not already Puppet::Error (permission denied, read-only filesystem, ENOSPC) are logged and re-raised as Puppet::Error '%{klass} could not write %{path}: %{detail}'. The cache is only marked synced (@synced = Time.now) when the write succeeds.","triggerScenarios":"Puppet::Util::FileType.filetype(:flat).new('/etc/app.conf').write(text) as a user without write permission, on a read-only mount, or when the target's parent directory is missing; crontab writes failing through the cron binary hit the same wrapper.","commonSituations":"Agents running least-privilege but managing root-owned files, immutable/append-only files (chattr +i), containers with read-only /etc, and full disks.","solutions":["Verify write permission and filesystem mutability for the agent user (ls -l, lsattr)","Ensure the parent directory exists and the file is not immutable","Rescue Puppet::Error at the call site, report it, and remediate (permissions/mount) before retrying"],"exampleFix":"# before\nPuppet::Util::FileType.filetype(:flat).new('/etc/motd').write(text)\n# => Puppet::Error: ... could not write /etc/motd: Read-only file system\n\n# after\nbegin\n  Puppet::Util::FileType.filetype(:flat).new('/etc/motd').write(text)\nrescue Puppet::Error => e\n  Puppet.err(\"motd not updated: #{e.message}\")\nend","handlingStrategy":"try-catch","validationCode":"writable = File.exist?(path) ? File.writable?(path) : File.writable?(File.dirname(path))\nraise ArgumentError, \"#{path} not writable\" unless writable\nft.write(text)","typeGuard":null,"tryCatchPattern":"begin\n  ft.write(text)\nrescue Puppet::Error => e\n  Puppet.err(\"write failed: #{e.message}\")\n  raise unless e.message =~ /Resource temporarily unavailable/\nend","preventionTips":["Check File.writable? on the file or its parent directory before writing","Avoid chattr +i and read-only mounts on managed paths","Rescue Puppet::Error around writes; the original cause is embedded in the message"],"tags":["puppet","file-io","permissions","filetype","write"],"backgroundTag":"file-write-failed","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}