sinatra/sinatra · error · IOError
not opened for reading
Error message
not opened for reading
What it means
IOError raised by not_open_for_reading (sinatra-contrib/lib/sinatra/streaming.rb:187) and aliased to every read-side IO method: read, gets, getc, getbyte, readline, readlines, readchar, readbyte, read_nonblock, readpartial, sysread, ungetbyte, eof, eof?, bytes. Because the Sinatra stream is write-only, any read attempt is rejected outright.
Source
Thrown at sinatra-contrib/lib/sinatra/streaming.rb:187
closed?
end
def external_encoding
Encoding.find settings.default_encoding
rescue NameError
settings.default_encoding
end
def settings
app.settings
end
def rewind
@pos = @lineno = 0
end
def not_open_for_reading(*)
raise IOError, 'not opened for reading'
end
alias bytes not_open_for_reading
alias eof? not_open_for_reading
alias eof not_open_for_reading
alias getbyte not_open_for_reading
alias getc not_open_for_reading
alias gets not_open_for_reading
alias read not_open_for_reading
alias read_nonblock not_open_for_reading
alias readbyte not_open_for_reading
alias readchar not_open_for_reading
alias readline not_open_for_reading
alias readlines not_open_for_reading
alias readpartial not_open_for_reading
alias sysread not_open_for_reading
alias ungetbyte not_open_for_reading
alias ungetc not_open_for_readingView on GitHub (pinned to cb22afd790)
Solutions
- Do not read from a Sinatra stream - it is write-only by design; track completion via closed? instead of eof?.
- Buffer what you need into a local String/StringIO and read from that, writing the result to the stream.
- If a library insists on reading, pass it a Tempfile/StringIO and stream the tempfile's contents out.
- Wrap unmodifiable third-party read calls in rescue IOError with a sensible fallback.
Example fix
# before stream do |out| chunk = out.read # write-only stream -> IOError end # after stream do |out| buffer = StringIO.new chunk = buffer.read out.write chunk end
Defensive patterns
Strategy: try-catch
Validate before calling
raise IOError, 'stream is write-only' if %i[read gets getc eof? readlines].any? { |m| caller_locations(1,1)[0].label == m.to_s } Type guard
readable_io = ->(io) { io.respond_to?(:read) && !io.is_a?(Sinatra::Streaming::Stream) } Try / catch
begin data = out.read rescue IOError data = nil # write-only stream; nothing to read end
Prevention
- Treat Sinatra streams as write-only; never call read/gets/getc/eof? on them.
- Use closed?, not eof?, to detect stream completion.
- Buffer into StringIO/Tempfile when a library needs to read back data.
- Wrap third-party read calls in rescue IOError with a defined fallback.
When it happens
Trigger: Calling out.read, out.gets, out.eof?, or any read method on a streaming response body; or handing the stream to code that reads from the IO it is given.
Common situations: Passing the stream to a library expecting a readable IO ( serializers, loggers, HTTP clients that read back the body), confusing the write stream with a bidirectional socket/file, or calling eof? to check completion on the wrong side.
Related errors
AI-assisted analysis of sinatra/sinatra@cb22afd790 (2026-08-04).
Data as JSON: /data/errors/c158b9969c8465d5.json.
Report an issue: GitHub.