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

out_buffer is required

Error message

out_buffer is required

What it means

Puppet::Util::Windows::File.device_io_control raises Puppet::Util::Windows::Error('out_buffer is required') when called with out_buffer nil - DeviceIoControl always needs a destination for returned bytes, so this is a programmer error at the call site, not a system failure. No Win32 call has been made yet when it raises.

Source

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

  end

  def self.get_reparse_point_tag(handle)
    reparse_tag = nil

    # 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)

      # DWORD ReparseTag is the first member of the struct
      reparse_tag = reparse_data_buffer_ptr.read_win32_ulong
    end

    reparse_tag
  end

  def self.device_io_control(handle, io_control_code, in_buffer = nil, out_buffer = nil)
    if out_buffer.nil?
      raise Puppet::Util::Windows::Error, _("out_buffer is required")
    end

    FFI::MemoryPointer.new(:dword, 1) do |bytes_returned_ptr|
      result = DeviceIoControl(
        handle,
        io_control_code,
        in_buffer, in_buffer.nil? ? 0 : in_buffer.size,
        out_buffer, out_buffer.size,
        bytes_returned_ptr,
        nil
      )

      if result == FFI::WIN32_FALSE
        raise Puppet::Util::Windows::Error, "DeviceIoControl(#{handle}, #{io_control_code}, " \
                                            "#{in_buffer}, #{in_buffer ? in_buffer.size : ''}, " \
                                            "#{out_buffer}, #{out_buffer ? out_buffer.size : ''}"
      end
    end

View on GitHub (pinned to e227c27540)

Solutions

  1. Always pass an out_buffer, e.g. FFI::MemoryPointer.new(MAXIMUM_REPARSE_DATA_BUFFER_SIZE) (>= 10240 bytes for reparse data)
  2. Read returned byte counts from the API result/overlapped struct rather than inferring from buffer size
  3. Treat this raise as a defect signal in your own wrapper - fix the call, do not rescue it

Example fix

// before
Puppet::Util::Windows::File.device_io_control(handle, FSCTL_GET_REPARSE_POINT, nil)  # nil out_buffer

// after
FFI::MemoryPointer.new(10240) do |out|
  Puppet::Util::Windows::File.device_io_control(handle, FSCTL_GET_REPARSE_POINT, nil, out)
end
Defensive patterns

Strategy: validation

Validate before calling

raise ArgumentError, 'device_io_control requires out_buffer' if out_buffer.nil?
out_buffer ||= FFI::MemoryPointer.new(Puppet::Util::Windows::File::MAXIMUM_REPARSE_DATA_BUFFER_SIZE)

Try / catch

begin
  Puppet::Util::Windows::File.device_io_control(handle, code, in_buffer, out_buffer)
rescue Puppet::Util::Windows::Error => e
  raise unless e.message.include?('out_buffer is required')
  raise ArgumentError, 'fix call site: out_buffer is mandatory'
end

Prevention

When it happens

Trigger: Calling device_io_control(handle, code, in_buffer) with only three arguments (out_buffer defaults to nil), or explicitly passing nil while intending an in-place buffer. Typical when adapting sample code for codes that only write output (e.g. FSCTL_GET_REPARSE_POINT).

Common situations: First-time FFI wrappers around DeviceIoControl assuming output is optional; refactors that dropped the fourth argument; copy-paste from in-buffer-only IOCTL examples.

Understand the failure class

Background: Missing required parameter errors: what 'X is required' and 'the required X param is missing' mean, and how to fix them — this error's family across 27 libraries.

Related errors


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