bumptech/glide · error · IOException
BufferedInputStream is closed
Error message
BufferedInputStream is closed
What it means
IOException thrown by RecyclableBufferedInputStream.available() after the stream has been closed (buf==null or underlying in==null). Closing releases the buffer; any subsequent read/available call indicates use-after-close of a recycled stream.
Source
Thrown at library/src/main/java/com/bumptech/glide/load/resource/bitmap/RecyclableBufferedInputStream.java:92
* Returns an estimated number of bytes that can be read or skipped without blocking for more
* input. This method returns the number of bytes available in the buffer plus those available in
* the source stream, but see {@link InputStream#available} for important caveats.
*
* @return the estimated number of bytes available
* @throws IOException if this stream is closed or an error occurs
*/
@Override
public synchronized int available() throws IOException {
// in could be invalidated by close().
InputStream localIn = in;
if (buf == null || localIn == null) {
throw streamClosed();
}
return count - pos + localIn.available();
}
private static IOException streamClosed() throws IOException {
throw new IOException("BufferedInputStream is closed");
}
/**
* Reduces the mark limit to match the current buffer length to prevent the buffer from continuing
* to increase in size.
*
* <p>Subsequent calls to {@link #mark(int)} will be obeyed and may cause the buffer size to
* increase.
*/
// Public API.
@SuppressWarnings("WeakerAccess")
public synchronized void fixMarkLimit() {
marklimit = buf.length;
}
public synchronized void release() {
if (buf != null) {
byteArrayPool.put(buf);View on GitHub (pinned to eb14a895d8)
Solutions
- Do not retain or reuse an InputStream after Glide has consumed it; fetch a fresh stream per load.
- In custom DataFetchers, open a new stream in loadData() each time and let Glide close it.
- Remove any explicit .close() on streams you hand to Glide.
- If pooling buffers, ensure the wrapper stream is not also exposed to callers.
Example fix
// before InputStream is = assetManager.open(path); Glide.with(ctx).load(is).into(img); is.available(); // closed by Glide already // after // Do not use the stream after passing it to Glide; reload if needed. Glide.with(ctx).load(new InputStreamLoader(assetManager, path)).into(img);
Defensive patterns
Strategy: validation
Validate before calling
// Do not call available()/read() on a stream you gave to Glide.
// If you must reuse, open a fresh stream:
InputStream fresh() { return assetManager.open(path); } Try / catch
try { is.available(); }
catch (IOException closed) {
// stream was closed by Glide; reopen if still needed
is = reopen();
} Prevention
- Treat any InputStream handed to Glide as owned by Glide.
- Open fresh streams per load in custom DataFetchers.
- Avoid sharing pooled streams across decoders.
When it happens
Trigger: Calling available() on a RecyclableBufferedInputStream after close() has been invoked. Common when an InputStream is fetched from a pool or wrapped and then closed by one decoder while another (or a retry) still references it.
Common situations: Reusing a cached/registry InputStream after the previous Glide decode pass closed it. Concurrent reads where one thread closes the stream. Custom ModelLoader/DataFetcher that returns a stream it then closes itself.
Related errors
- Stream 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/20d4635f4f847f40.
Report an issue: GitHub.