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

CreateFile(#{file_name}, #{desired_access.to_s(8)}, #{share_

Error message

CreateFile(#{file_name}, #{desired_access.to_s(8)}, #{share_mode.to_s(8)}, #{security_attributes}, #{creation_disposition.to_s(8)}, #{flags_and_attributes.to_s(8)}, #{template_file_handle})

What it means

Puppet::Util::Windows::File.create_file wraps CreateFileW and raises Puppet::Util::Windows::Error when the returned handle equals INVALID_HANDLE_VALUE. The message dumps every argument (access, share mode, security attributes, disposition, flags - most in octal) so you can reproduce the exact open. e.code carries the Win32 reason: not-found, access denied, sharing violation, or invalid parameter combination.

Source

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

    success = SetFileAttributesW(wide_string(path), flags) != FFI::WIN32_FALSE
    raise Puppet::Util::Windows::Error, _("Failed to set file attributes") unless success

    success
  end
  module_function :set_attributes

  # define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1)
  INVALID_HANDLE_VALUE = FFI::Pointer.new(-1).address
  def self.create_file(file_name, desired_access, share_mode, security_attributes,
                       creation_disposition, flags_and_attributes, template_file_handle)

    result = CreateFileW(wide_string(file_name.to_s),
                         desired_access, share_mode, security_attributes, creation_disposition,
                         flags_and_attributes, template_file_handle)

    return result unless result == INVALID_HANDLE_VALUE

    raise Puppet::Util::Windows::Error, "CreateFile(#{file_name}, #{desired_access.to_s(8)}, #{share_mode.to_s(8)}, " \
                                        "#{security_attributes}, #{creation_disposition.to_s(8)}, " \
                                        "#{flags_and_attributes.to_s(8)}, #{template_file_handle})"
  end

  def self.get_reparse_point_data(handle, &block)
    # must be multiple of 1024, min 10240
    FFI::MemoryPointer.new(MAXIMUM_REPARSE_DATA_BUFFER_SIZE) do |reparse_data_buffer_ptr|
      device_io_control(handle, FSCTL_GET_REPARSE_POINT, nil, reparse_data_buffer_ptr)

      reparse_tag = reparse_data_buffer_ptr.read_win32_ulong
      buffer_type = case reparse_tag
                    when IO_REPARSE_TAG_SYMLINK
                      SYMLINK_REPARSE_DATA_BUFFER
                    when IO_REPARSE_TAG_MOUNT_POINT
                      MOUNT_POINT_REPARSE_DATA_BUFFER
                    when IO_REPARSE_TAG_NFS
                      raise Puppet::Util::Windows::Error, "Retrieving NFS reparse point data is unsupported"
                    else

View on GitHub (pinned to e227c27540)

Solutions

  1. Decode e.code first: 2/3 path, 5 rights, 32 lock, 87 bad flag/disposition combo
  2. Ensure the disposition matches reality (CREATE_ALWAYS vs OPEN_EXISTING)
  3. Match share_mode with how other holders opened the file, or wait for the lock
  4. Run elevated for system files; enable long-path support (gpedit/registry LongPathsEnabled) for deep paths

Example fix

// before
handle = Puppet::Util::Windows::File.create_file(path, GENERIC_READ, 0, nil, OPEN_EXISTING, 0, 0)

// after
begin
  handle = Puppet::Util::Windows::File.create_file(path, GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0)
rescue Puppet::Util::Windows::Error => e
  raise Puppet::Error, "cannot open #{path}: win32 code #{e.code}" 
end
Defensive patterns

Strategy: try-catch

Validate before calling

raise ArgumentError, 'path missing' unless File.exist?(path)
share_mode ||= Puppet::FFI::Windows::Constants::FILE_SHARE_READ | Puppet::FFI::Windows::Constants::FILE_SHARE_WRITE

Try / catch

begin
  handle = Puppet::Util::Windows::File.create_file(path, desired_access, share_mode, security_attributes,
                                                 creation_disposition, flags_and_attributes, template_file_handle)
rescue Puppet::Util::Windows::Error => e
  raise "#{path}: not found" if [2, 3].include?(e.code)
  raise "#{path}: access denied" if e.code == 5
  raise "#{path}: locked by another process" if e.code == 32
  raise
end

Prevention

When it happens

Trigger: Opening a nonexistent path (2/3) with an OPEN_EXISTING-style disposition; requesting GENERIC_WRITE on admin-owned files unelevated (5); opening a file another process holds without a compatible share_mode (32); passing mutually exclusive creation_disposition/flags_and_attributes; opening a symlink without FILE_FLAG_OPEN_REPARSE_POINT; paths beyond MAX_PATH without long-path enablement.

Common situations: Puppet opening files for content replacement under Program Files as a filtered token; reparse-point inspection (this wrapper feeds get_reparse_point_data) where the link was removed between stat and open; backup software holding exclusive locks; long nested module paths exceeding 260 characters.

Related errors


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