{"record":{"id":"3a952f101ab8c9d1","repo":"bumptech/glide","slug":"stream-is-closed","errorCode":null,"errorMessage":"Stream is closed","messagePattern":"Stream is closed","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"library/src/main/java/com/bumptech/glide/load/resource/bitmap/RecyclableBufferedInputStream.java","lineNumber":341,"sourceCode":"      }\n      if (localIn.available() == 0) {\n        return byteCount - required;\n      }\n      offset += read;\n    }\n  }\n\n  /**\n   * Resets this stream to the last marked location.\n   *\n   * @throws IOException if this stream is closed, no mark has been put or the mark is no longer\n   *     valid because more than {@code readlimit} bytes have been read since setting the mark.\n   * @see #mark(int)\n   */\n  @Override\n  public synchronized void reset() throws IOException {\n    if (buf == null) {\n      throw new IOException(\"Stream is closed\");\n    }\n    if (-1 == markpos) {\n      throw new InvalidMarkException(\n          \"Mark has been invalidated, pos: \" + pos + \" markLimit: \" + marklimit);\n    }\n    pos = markpos;\n  }\n\n  /**\n   * Skips {@code byteCount} bytes in this stream. Subsequent calls to {@link #read} will not return\n   * these bytes unless {@link #reset} is used.\n   *\n   * @param byteCount the number of bytes to skip. This method does nothing and returns 0 if {@code\n   *     byteCount} is less than zero.\n   * @return the number of bytes actually skipped.\n   * @throws IOException if this stream is closed or another IOException occurs.\n   */\n  @Override","sourceCodeStart":323,"sourceCodeEnd":359,"githubUrl":"https://github.com/bumptech/glide/blob/eb14a895d8f866e6a08c3e19d50ea3bd2c12c20a/library/src/main/java/com/bumptech/glide/load/resource/bitmap/RecyclableBufferedInputStream.java#L323-L359","documentation":"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.","triggerScenarios":"Calling reset() after close(), or after the buffer was released to the ArrayPool. Marks cannot be honored once the backing array is gone.","commonSituations":"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.","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."],"exampleFix":"// before\nis.mark(1024);\n// ... other code calls is.close()\nis.reset(); // throws\n// after\nis.mark(1024);\ntry { /* read */ } finally { /* keep stream open until reset done */ }\nis.reset();\nis.close();","handlingStrategy":"try-catch","validationCode":"// Ensure the stream is open before reset.\nboolean isOpen(RecyclableBufferedInputStream is) {\n  // no public isOpen; track close() yourself\n  return !closedFlag;\n}","typeGuard":null,"tryCatchPattern":"try { is.reset(); }\ncatch (IOException e) {\n  // stream closed between mark and reset: reopen and retry from scratch\n}","preventionTips":["Never close a stream between mark() and reset().","Perform mark/read/reset within a single try block.","Open fresh streams per decode attempt."],"tags":["stream","io","lifecycle","android"],"backgroundTag":null,"analyzedSha":"eb14a895d8f866e6a08c3e19d50ea3bd2c12c20a","analyzedAt":"2026-08-14T01:43:54.821Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}