{"record":{"id":"220425759fd53bb4","repo":"puppetlabs/puppet","slug":"replacefile-target-source","errorCode":null,"errorMessage":"ReplaceFile(#{target}, #{source})","messagePattern":"ReplaceFile\\(#(.+?), #(.+?)\\)","errorType":"exception","errorClass":"Puppet::Util::Windows::Error","httpStatus":null,"severity":"error","filePath":"lib/puppet/util/windows/file.rb","lineNumber":33,"sourceCode":"\n  def replace_file(target, source)\n    target_encoded = wide_string(target.to_s)\n    source_encoded = wide_string(source.to_s)\n\n    flags = REPLACEFILE_IGNORE_MERGE_ERRORS\n    backup_file = nil\n    result = ReplaceFileW(\n      target_encoded,\n      source_encoded,\n      backup_file,\n      flags,\n      FFI::Pointer::NULL,\n      FFI::Pointer::NULL\n    )\n\n    return true if result != FFI::WIN32_FALSE\n\n    raise Puppet::Util::Windows::Error, \"ReplaceFile(#{target}, #{source})\"\n  end\n  module_function :replace_file\n\n  def move_file_ex(source, target, flags = 0)\n    result = MoveFileExW(wide_string(source.to_s),\n                         wide_string(target.to_s),\n                         flags)\n\n    return true if result != FFI::WIN32_FALSE\n\n    raise Puppet::Util::Windows::Error, \"MoveFileEx(#{source}, #{target}, #{flags.to_s(8)})\"\n  end\n  module_function :move_file_ex\n\n  def symlink(target, symlink)\n    flags = File.directory?(target) ? 0x1 : 0x0\n    result = CreateSymbolicLinkW(wide_string(symlink.to_s),\n                                 wide_string(target.to_s), flags)","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/util/windows/file.rb#L15-L51","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Check e.code: 2/3 = target or source missing, 5 = access denied, 32 = file locked","Verify both target and source exist and are on the same volume before calling","Match or clear hidden/system/read-only attributes on the target before replacing","Run elevated (or grant write ACL) when replacing protected files; retry once on code 32 to ride out AV scans"],"exampleFix":"// before\nPuppet::Util::Windows::File.replace_file(target, source)  # raises on first failure\n\n// after\nbegin\n  Puppet::Util::Windows::File.replace_file(target, source)\nrescue Puppet::Util::Windows::Error => e\n  Puppet.warning(\"replace_file failed (#{e.code}): #{e.message}\")\n  raise if [2, 3, 5].include?(e.code) # do not retry structural failures\nend","handlingStrategy":"try-catch","validationCode":"INVALID = Puppet::Util::Windows::File::INVALID_FILE_ATTRIBUTES\nok = [target, source].all? { |p| Puppet::Util::Windows::File.get_attributes(p, false) != INVALID }\nraise ArgumentError, 'target/source missing or inaccessible' unless ok","typeGuard":null,"tryCatchPattern":"begin\n  Puppet::Util::Windows::File.replace_file(target, source)\nrescue Puppet::Util::Windows::Error => e\n  case e.code\n  when 2, 3 then raise \"replace_file: target or source missing\"\n  when 5 then raise \"replace_file: access denied (elevate?)\"\n  when 32 then sleep 1; retry if (tries += 1) < 3  # AV lock\n  else raise\n  end\nend","preventionTips":["Verify both paths exist and share a volume before replacing","Align hidden/system attributes between target and replacement before the swap","Treat code 32 (sharing violation) as transient - retry briefly before failing"],"tags":["windows","file-system","win32-api","ffi","file-replace","puppet"],"backgroundTag":"win32-file-replace-failed","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}