bumptech/glide · error · InvalidMarkException

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

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.

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)

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.