{"record":{"id":"84899e2714eeec52","repo":"puppetlabs/puppet","slug":"is-a-directory-directory-84899e","errorCode":null,"errorMessage":"Is a directory: %{directory}","messagePattern":"Is a directory: %(.+?)","errorType":"exception","errorClass":"Errno::EISDIR","httpStatus":null,"severity":"error","filePath":"lib/puppet/file_system/windows.rb","lineNumber":140,"sourceCode":"  end\n\n  def read_preserve_line_endings(path)\n    contents = path.read(:mode => 'rb', :encoding => 'bom|utf-8')\n    contents = path.read(:mode => 'rb', :encoding => \"bom|#{Encoding.default_external.name}\") unless contents.valid_encoding?\n    contents = path.read unless contents.valid_encoding?\n\n    contents\n  end\n\n  # https://docs.microsoft.com/en-us/windows/desktop/debug/system-error-codes--0-499-\n  FILE_NOT_FOUND = 2\n  ACCESS_DENIED = 5\n  SHARING_VIOLATION = 32\n  LOCK_VIOLATION = 33\n\n  def replace_file(path, mode = nil)\n    if directory?(path)\n      raise Errno::EISDIR, _(\"Is a directory: %{directory}\") % { directory: path }\n    end\n\n    current_sid = Puppet::Util::Windows::SID.name_to_sid(Puppet::Util::Windows::ADSI::User.current_user_name)\n    current_sid ||= Puppet::Util::Windows::SID.name_to_sid(Puppet::Util::Windows::ADSI::User.current_sam_compatible_user_name)\n\n    dacl = case mode\n           when 0o644\n             dacl = secure_dacl(current_sid)\n             dacl.allow(Puppet::Util::Windows::SID::BuiltinUsers, FILE_READ)\n             dacl\n           when 0o660, 0o640, 0o600, 0o440\n             secure_dacl(current_sid)\n           when nil\n             get_dacl_from_file(path) || secure_dacl(current_sid)\n           else\n             raise ArgumentError, \"#{mode} is invalid: Only modes 0644, 0640, 0660, and 0440 are allowed\"\n           end\n","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/file_system/windows.rb#L122-L158","documentation":"Puppet::FileSystem::Windows#replace_file refuses to atomically replace a path that is a directory, raising Errno::EISDIR with the path, mirroring POSIX rename semantics. The subsequent logic (DACL/inheritance handling) only makes sense for files, so a directory destination is rejected up front.","triggerScenarios":"An atomic file write on Windows targeting a path that already exists as a directory: config/state files whose path collides with a folder created by an installer or another resource; Puppet manifests writing files where ensure directory was previously applied.","commonSituations":"A file resource path that was changed from a directory to a file without cleaning up; installers creating placeholder directories; case-insensitive path collisions on Windows.","solutions":["Check the path on the node: if a directory occupies it, remove or rename it (`Remove-Item -Recurse`)","Correct the resource design so files and directories never share one path","If the directory is managed by Puppet, ensure => absent it (as a directory) in a preceding step before writing the file"],"exampleFix":"# before (manifest)\nfile { 'C:/opt/app/state': ensure => directory }\nfile { 'C:/opt/app/state': ensure => file, source => 'puppet:///modules/app/state' }\n\n# after\nfile { 'C:/opt/app/state': ensure => absent, force => true }\nfile { 'C:/opt/app/state.txt': ensure => file, source => 'puppet:///modules/app/state' }","handlingStrategy":"validation","validationCode":"if Puppet::FileSystem.directory?(path)\n  raise ArgumentError, \"#{path} is a directory; cannot replace with a file\"\nend\nPuppet::FileSystem.replace_file(path, 0o644) { |f| f.write(data) }","typeGuard":"def replaceable_path?(path)\n  !Puppet::FileSystem.exist?(path) || Puppet::FileSystem.file?(path)\nend","tryCatchPattern":"begin\n  Puppet::FileSystem.replace_file(path, 0o644) { |f| f.write(data) }\nrescue Errno::EISDIR => e\n  raise \"#{path} occupied by a directory (#{e.message}); remove it or pick a new file name\"\nend","preventionTips":["Keep a naming scheme where directories and files never collide (extensions on files)","Audit installer-created placeholder folders before writing config into their paths","Remember Windows paths are case-insensitive: collisions can hide behind casing"],"tags":["puppet","windows","file-system","eisdir","atomic-write"],"backgroundTag":"is-a-directory-error","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}