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

  1. Do not retain or reuse an InputStream after Glide has consumed it; fetch a fresh stream per load.
  2. In custom DataFetchers, open a new stream in loadData() each time and let Glide close it.
  3. Remove any explicit .close() on streams you hand to Glide.
  4. 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

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


AI-assisted analysis of bumptech/glide@eb14a895d8 (2026-08-14). Data as JSON: /api/errors/20d4635f4f847f40. Report an issue: GitHub.