ruby-grape/grape · error · ArgumentError

Argument must be a file path

Error message

Argument must be a file path

What it means

`sendfile` hands a file to the client via Grape::ServeStream::FileBody, which needs a filesystem path it can open lazily. The guard requires the argument to be a String; File handles, Pathname objects, or any IO-like object raise 'Argument must be a file path'. Note this is stricter than Rack, which also accepts objects responding to to_path.

Source

Thrown at lib/grape/dsl/inside_route.rb:125

      #   end
      #
      #   DELETE /12 # => 204 No Content, ""
      def return_no_content
        body false
      end

      # Allows you to send a file to the client via sendfile.
      #
      # @example
      #   get '/file' do
      #     sendfile FileStreamer.new(...)
      #   end
      #
      #   GET /file # => "contents of file"
      def sendfile(value = nil)
        return stream if value.nil?

        raise ArgumentError, 'Argument must be a file path' unless value.is_a?(String)

        file_body = Grape::ServeStream::FileBody.new(value)
        @stream = Grape::ServeStream::StreamResponse.new(file_body)
      end

      # Allows you to define the response as a streamable object.
      #
      # If Content-Length and Transfer-Encoding are blank (among other conditions),
      # Rack assumes this response can be streamed in chunks.
      #
      # @example
      #   get '/stream' do
      #     stream FileStreamer.new(...)
      #   end
      #
      #   GET /stream # => "chunked contents of file"
      #
      # See:

View on GitHub (pinned to 22d7975629)

Solutions

  1. Pass the path string: `sendfile file.path` for File/Tempfile, `sendfile pathname.to_s` for Pathname.
  2. If you have an IO object rather than a path, use the `stream` helper with an each-responding wrapper instead of `sendfile`.

Example fix

# before
sendfile Pathname.new('/files/report.pdf') # raises
sendfile uploaded_tempfile            # Tempfile object raises

# after
sendfile Pathname.new('/files/report.pdf').to_s
sendfile uploaded_tempfile.path
Defensive patterns

Strategy: type-guard

Validate before calling

path = case value
       when String then value
       when File, Tempfile then value.path
       when Pathname then value.to_s
       else raise ArgumentError, "cannot derive a file path from #{value.class}"
       end
sendfile path

Type guard

def sendfile_path?(value) = value.is_a?(String) && !value.empty?

Prevention

When it happens

Trigger: `sendfile File.open('/tmp/x.csv')` (File object). `sendfile Pathname.new('/tmp/x.csv')` (Pathname). `sendfile tempfile` where tempfile is a Tempfile from a multipart upload instead of `tempfile.path`.

Common situations: Streaming generated reports or uploads stored on disk. Reusing Rack/Sinatra send_file idioms that accept Pathname or File. Passing the result of another API that returns a file handle.

Related errors


AI-assisted analysis of ruby-grape/grape@22d7975629 (2026-08-21). Data as JSON: /api/errors/cf9a771da0edf940. Report an issue: GitHub.