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
- Pass an Enumerator, e.g. `stream_body Enumerator.new { |y| ... }` or `stream_body collection.each`.
- Implement `each` (and ideally include Enumerable) on the custom stream object.
- 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
- Give custom iterators `include Enumerable` plus `each` by construction.
- Prefer Enumerators (`Enumerator.new`, `collection.each`) for generated streams.
- Spec streaming endpoints with a body-consuming assertion so a non-each object fails tests.
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
- type #{type} should support coercion via `[]`
- #declared is not available prior to parameter validation
- Representation of type #{representation.class} cannot be mer
- Status code must be Integer or Symbol.
- Argument must be a file path
AI-assisted analysis of ruby-grape/grape@22d7975629 (2026-08-21).
Data as JSON: /api/errors/8a004910e89e477c.
Report an issue: GitHub.