puppetlabs/puppet · error · Puppet::Error

Could not remove existing file

Error message

Could not remove existing file

What it means

Inside mklink, Puppet first calls remove_existing(target) to clear whatever occupies the link path, then verifies with Puppet::FileSystem.exist? that the path is actually gone. If the entry survived removal (permissions, read-only attributes, open handles, non-empty directory), it raises 'Could not remove existing file' rather than letting symlink(2) fail obscurely.

Source

Thrown at lib/puppet/type/file/target.rb:45

    newvalue(/./) do
      @resource[:ensure] = :link unless @resource.should(:ensure)

      # Only call mklink if ensure didn't call us in the first place.
      currentensure = @resource.property(:ensure).retrieve
      mklink if @resource.property(:ensure).safe_insync?(currentensure)
    end

    # Create our link.
    def mklink
      raise Puppet::Error, "Cannot symlink on this platform version" unless provider.feature?(:manages_symlinks)

      target = should

      # Clean up any existing objects.  The argument is just for logging,
      # it doesn't determine what's removed.
      @resource.remove_existing(target)

      raise Puppet::Error, "Could not remove existing file" if Puppet::FileSystem.exist?(@resource[:path])

      Puppet::Util::SUIDManager.asuser(@resource.asuser) do
        mode = @resource.should(:mode)
        if mode
          Puppet::Util.withumask(0o00) do
            Puppet::FileSystem.symlink(target, @resource[:path])
          end
        else
          Puppet::FileSystem.symlink(target, @resource[:path])
        end
      end

      @resource.send(:property_fix)

      :link_created
    end

    def insync?(currentvalue)

View on GitHub (pinned to e227c27540)

Solutions

  1. Inspect the path: ls -ld (POSIX) or handle.exe (Windows) and clear the entry manually.
  2. Manage the conflicting object in Puppet: a file resource with ensure => absent ordered before the link.
  3. On Windows, remove read-only attributes, close locking processes, and run elevated.
  4. If the entry is a directory, remove it (or manage its removal) before creating the link.

Example fix

// before: /opt/app/current occupied by a locked file
file { '/opt/app/current':
  ensure => link,
  target => '/opt/app/releases/2026.08',
}

// after: manage removal first, then the link
file { '/opt/app/current': ensure => absent }
->
file { '/opt/app/current':
  ensure => link,
  target => '/opt/app/releases/2026.08',
}
Defensive patterns

Strategy: validation

Validate before calling

# Ruby pre-flight before creating a link
if Puppet::FileSystem.exist?(link_path)
  st = File.lstat(link_path)
  fail("cannot clear #{link_path} (#{st.ftype}) — remove it or manage ensure => absent first") unless File.writable?(File.dirname(link_path))
end

Try / catch

rescue Puppet::Error on /Could not remove existing file/; lstat the path, close locks / clear read-only attributes / pre-remove the entry, then re-run the agent — creation is idempotent.

Prevention

When it happens

Trigger: `ensure => link` onto a path occupied by a file the agent cannot delete (EACCES/EPERM); a Windows file with the read-only attribute or an open handle; the existing entry being a non-empty directory; another process re-creating the path between removal and the check.

Common situations: Windows agents hitting locked/read-only files (AV scanners, backup agents); agent running non-root over root-owned entries; deployment layouts where 'current' exists as a directory from a previous release.

Related errors


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