{"record":{"id":"f4634b0d245b1981","repo":"bumptech/glide","slug":"mark-has-been-invalidated-pos-pos-marklimit","errorCode":null,"errorMessage":"Mark has been invalidated, pos: {pos} markLimit: {marklimit}","messagePattern":"Mark has been invalidated, pos: (.+?) markLimit: (.+?)","errorType":"exception","errorClass":"InvalidMarkException","httpStatus":null,"severity":"error","filePath":"library/src/main/java/com/bumptech/glide/load/resource/bitmap/RecyclableBufferedInputStream.java","lineNumber":344,"sourceCode":"      }\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\n  public synchronized long skip(long byteCount) throws IOException {\n    if (byteCount < 1) {\n      return 0;","sourceCodeStart":326,"sourceCodeEnd":362,"githubUrl":"https://github.com/bumptech/glide/blob/eb14a895d8f866e6a08c3e19d50ea3bd2c12c20a/library/src/main/java/com/bumptech/glide/load/resource/bitmap/RecyclableBufferedInputStream.java#L326-L362","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Increase the marklimit passed to mark() to exceed the maximum bytes you will read before reset().","Use a larger buffer size when constructing RecyclableBufferedInputStream.","Avoid long read sequences between mark and reset; reset earlier or re-open the stream.","For format detection, read the whole header into a byte array first, then parse from memory."],"exampleFix":"// before\nis.mark(64);\nreadFullHeader(is); // reads > 64 bytes\nis.reset(); // throws InvalidMarkException\n// after\nis.mark(HEADER_MAX_SIZE); // e.g. 8192\nreadFullHeader(is);\nis.reset();","handlingStrategy":"validation","validationCode":"// Size marklimit to the maximum bytes you will read before reset.\nint maxReadBeforeReset = computeMaxHeaderSize();\nis.mark(maxReadBeforeReset);","typeGuard":null,"tryCatchPattern":"try { is.reset(); }\ncatch (RecyclableBufferedInputStream.InvalidMarkException e) {\n  // mark invalidated: reopen the stream and re-decode from start\n}","preventionTips":["Set marklimit larger than any read you perform before reset.","Reset as soon as you can, not after long reads.","For header sniffing, read the whole header into memory first."],"tags":["stream","io","mark-reset","android"],"backgroundTag":null,"analyzedSha":"eb14a895d8f866e6a08c3e19d50ea3bd2c12c20a","analyzedAt":"2026-08-14T01:43:54.821Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}