bumptech/glide · error · IOException
Stream is closed
Error message
Stream is closed
What it means
IOException thrown by RecyclableBufferedInputStream.reset() when buf==null, i.e. the stream has been closed. reset() refuses to operate on a released buffer because mark state is no longer valid.
Source
Thrown at library/src/main/java/com/bumptech/glide/load/resource/bitmap/RecyclableBufferedInputStream.java:341
}
if (localIn.available() == 0) {
return byteCount - required;
}
offset += read;
}
}
/**
* Resets this stream to the last marked location.
*
* @throws IOException if this stream is closed, no mark has been put or the mark is no longer
* valid because more than {@code readlimit} bytes have been read since setting the mark.
* @see #mark(int)
*/
@Override
public synchronized void reset() throws IOException {
if (buf == null) {
throw new IOException("Stream is closed");
}
if (-1 == markpos) {
throw new InvalidMarkException(
"Mark has been invalidated, pos: " + pos + " markLimit: " + marklimit);
}
pos = markpos;
}
/**
* Skips {@code byteCount} bytes in this stream. Subsequent calls to {@link #read} will not return
* these bytes unless {@link #reset} is used.
*
* @param byteCount the number of bytes to skip. This method does nothing and returns 0 if {@code
* byteCount} is less than zero.
* @return the number of bytes actually skipped.
* @throws IOException if this stream is closed or another IOException occurs.
*/
@OverrideView on GitHub (pinned to eb14a895d8)
Solutions
- Avoid reusing streams across decode attempts; open a fresh stream for each.
- In custom decoders, ensure mark/reset happen on the same open stream instance without intervening close().
- Do not return a closed stream from a DataFetcher.
Example fix
// before
is.mark(1024);
// ... other code calls is.close()
is.reset(); // throws
// after
is.mark(1024);
try { /* read */ } finally { /* keep stream open until reset done */ }
is.reset();
is.close(); Defensive patterns
Strategy: try-catch
Validate before calling
// Ensure the stream is open before reset.
boolean isOpen(RecyclableBufferedInputStream is) {
// no public isOpen; track close() yourself
return !closedFlag;
} Try / catch
try { is.reset(); }
catch (IOException e) {
// stream closed between mark and reset: reopen and retry from scratch
} Prevention
- Never close a stream between mark() and reset().
- Perform mark/read/reset within a single try block.
- Open fresh streams per decode attempt.
When it happens
Trigger: Calling reset() after close(), or after the buffer was released to the ArrayPool. Marks cannot be honored once the backing array is gone.
Common situations: Decoders that mark/reset on an InputStream that another component (or Glide itself) has closed between the mark and reset. Reusing a pooled stream across decode attempts.
Related errors
- BufferedInputStream is closed
- Mark has been invalidated, pos: {pos} markLimit: {marklimit}
- Cannot reset to unset mark position
- You cannot start a load on a null Context
- You cannot start a load for a destroyed activity
AI-assisted analysis of bumptech/glide@eb14a895d8 (2026-08-14).
Data as JSON: /api/errors/3a952f101ab8c9d1.
Report an issue: GitHub.