bumptech/glide · error · InvalidMarkException

Mark has been invalidated, pos

Error message

Mark has been invalidated, pos: {pos} markLimit: {marklimit}

What it means

InvalidMarkException (an IOException subclass) thrown by RecyclableBufferedInputStream.reset() when markpos == -1, meaning the mark has been invalidated because more than marklimit bytes were read between mark() and reset(). The buffered data was discarded to free memory.

Solutions

  1. Increase the marklimit passed to mark() to exceed the maximum bytes you will read before reset().
  2. Use a larger buffer size when constructing RecyclableBufferedInputStream.
  3. Avoid long read sequences between mark and reset; reset earlier or re-open the stream.
  4. For format detection, read the whole header into a byte array first, then parse from memory.

Example fix

// before
is.mark(64);
readFullHeader(is); // reads > 64 bytes
is.reset(); // throws InvalidMarkException
// after
is.mark(HEADER_MAX_SIZE); // e.g. 8192
readFullHeader(is);
is.reset();
Defensive patterns

Strategy: validation

Validate before calling

// Size marklimit to the maximum bytes you will read before reset.
int maxReadBeforeReset = computeMaxHeaderSize();
is.mark(maxReadBeforeReset);

Try / catch

try { is.reset(); }
catch (RecyclableBufferedInputStream.InvalidMarkException e) {
  // mark invalidated: reopen the stream and re-decode from start
}

Prevention

When it happens

Trigger: Calling mark(readlimit), then reading more than readlimit bytes, then calling reset(). The buffer cannot guarantee the marked position is still available, so reset is rejected.

Common situations: Decoders that probe far ahead of a small mark limit (e.g. header sniffing on formats with large headers). Custom InputStream wrappers with too-small mark limits passed to Glide.

Related errors


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

Appendix: source

Thrown at library/src/main/java/com/bumptech/glide/load/resource/bitmap/RecyclableBufferedInputStream.java:344

      }
      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.
   */
  @Override
  public synchronized long skip(long byteCount) throws IOException {
    if (byteCount < 1) {
      return 0;

View on GitHub (pinned to eb14a895d8)