ruby-grape/grape · error · ArgumentError

Stream object must respond to :each.

Error message

Stream object must respond to :each.

What it means

`stream_body` (used by the `stream` helper) wraps the response body for chunked streaming. Rack streams a body by calling `each`, so any non-String value passed here must respond to `each` - an Enumerator, an Enumerable, or a custom object implementing `each`. A String is accepted because FileBody treats it as a path; anything else lacking `each` raises 'Stream object must respond to :each.'.

Source

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

      def http_version
        env.fetch('HTTP_VERSION') { env[Rack::SERVER_PROTOCOL] }
      end

      def api_format(format)
        env[Grape::Env::API_FORMAT] = format
      end

      def context
        self
      end

      private

      # Wraps a stream +value+ into a body that responds to +:each+.
      def stream_body(value)
        return Grape::ServeStream::FileBody.new(value) if value.is_a?(String)

        raise ArgumentError, 'Stream object must respond to :each.' unless value.respond_to?(:each)

        value
      end

      # The default HTTP status when none has been set explicitly.
      def default_status
        return 201 if request.post?
        return 204 if request.delete? && @body.blank?

        200
      end
    end
  end
end

View on GitHub (pinned to 22d7975629)

Solutions

  1. Pass an Enumerator, e.g. `stream_body Enumerator.new { |y| ... }` or `stream_body collection.each`.
  2. Implement `each` (and ideally include Enumerable) on the custom stream object.
  3. If the value is actually a file path, pass the String and let FileBody handle it.

Example fix

# before
class Cursor
  def initialize(rows) = @rows = rows
  def next = @rows.shift
end
stream_body Cursor.new(rows) # raises, no :each

# after
class Cursor
  include Enumerable
  def initialize(rows) = @rows = rows
  def each(&) = @rows.each(&)
end
stream_body Cursor.new(rows)
Defensive patterns

Strategy: type-guard

Validate before calling

streamable = value.is_a?(String) || value.respond_to?(:each)
raise ArgumentError, "#{value.class} cannot stream (needs :each or a path String)" unless streamable
stream_body value

Type guard

def streamable?(value) = value.is_a?(String) || value.respond_to?(:each)

Prevention

When it happens

Trigger: `stream_body 42` or `stream_body some_struct` with a plain value object. `stream` with a wrapper object that forgot to implement `each` (e.g. a lazy generator exposing only `next`). Passing a Proc or lambda instead of an Enumerator.

Common situations: Streaming SSE/NDJSON endpoints using custom cursor or query objects. Wrapping a database cursor for chunked responses without including Enumerable. Refactoring from returning an array to a custom iterator.

Related errors


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