puppetlabs/puppet · error · Puppet::Util::Windows::Error

ReplaceFile(#{target}, #{source})

Error message

ReplaceFile(#{target}, #{source})

What it means

Puppet::Util::Windows::File.replace_file calls the Win32 ReplaceFileW API to swap `target` with `source` while preserving the target's ACLs, attributes, and object ID. If ReplaceFileW returns FALSE, this Puppet::Util::Windows::Error is raised; the class appends the formatted Win32 error text (via FormatMessageW) and exposes the numeric code as e.code (from FFI.errno). The message names target and source so you can tell which side of the operation failed.

Source

Thrown at lib/puppet/util/windows/file.rb:33

  def replace_file(target, source)
    target_encoded = wide_string(target.to_s)
    source_encoded = wide_string(source.to_s)

    flags = REPLACEFILE_IGNORE_MERGE_ERRORS
    backup_file = nil
    result = ReplaceFileW(
      target_encoded,
      source_encoded,
      backup_file,
      flags,
      FFI::Pointer::NULL,
      FFI::Pointer::NULL
    )

    return true if result != FFI::WIN32_FALSE

    raise Puppet::Util::Windows::Error, "ReplaceFile(#{target}, #{source})"
  end
  module_function :replace_file

  def move_file_ex(source, target, flags = 0)
    result = MoveFileExW(wide_string(source.to_s),
                         wide_string(target.to_s),
                         flags)

    return true if result != FFI::WIN32_FALSE

    raise Puppet::Util::Windows::Error, "MoveFileEx(#{source}, #{target}, #{flags.to_s(8)})"
  end
  module_function :move_file_ex

  def symlink(target, symlink)
    flags = File.directory?(target) ? 0x1 : 0x0
    result = CreateSymbolicLinkW(wide_string(symlink.to_s),
                                 wide_string(target.to_s), flags)

View on GitHub (pinned to e227c27540)

Solutions

  1. Check e.code: 2/3 = target or source missing, 5 = access denied, 32 = file locked
  2. Verify both target and source exist and are on the same volume before calling
  3. Match or clear hidden/system/read-only attributes on the target before replacing
  4. Run elevated (or grant write ACL) when replacing protected files; retry once on code 32 to ride out AV scans

Example fix

// before
Puppet::Util::Windows::File.replace_file(target, source)  # raises on first failure

// after
begin
  Puppet::Util::Windows::File.replace_file(target, source)
rescue Puppet::Util::Windows::Error => e
  Puppet.warning("replace_file failed (#{e.code}): #{e.message}")
  raise if [2, 3, 5].include?(e.code) # do not retry structural failures
end
Defensive patterns

Strategy: try-catch

Validate before calling

INVALID = Puppet::Util::Windows::File::INVALID_FILE_ATTRIBUTES
ok = [target, source].all? { |p| Puppet::Util::Windows::File.get_attributes(p, false) != INVALID }
raise ArgumentError, 'target/source missing or inaccessible' unless ok

Try / catch

begin
  Puppet::Util::Windows::File.replace_file(target, source)
rescue Puppet::Util::Windows::Error => e
  case e.code
  when 2, 3 then raise "replace_file: target or source missing"
  when 5 then raise "replace_file: access denied (elevate?)"
  when 32 then sleep 1; retry if (tries += 1) < 3  # AV lock
  else raise
  end
end

Prevention

When it happens

Trigger: replace_file(target, source) when target or source does not exist (code 2/3), the caller lacks write access to the target (code 5), the target has hidden/system attributes the replacement lacks, the files sit on different volumes, or another process (antivirus, indexer) holds the target open (code 32, sharing violation).

Common situations: Puppet agents running non-elevated replacing files under Program Files or system paths; targets previously marked read-only/hidden by another resource; antivirus locking files mid-swap during catalog application; long paths over 260 chars on older Windows.

Related errors


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