{"record":{"id":"78c3e203ca2a4ad2","repo":"didi/DoKit","slug":"cannot-reset","errorCode":null,"errorMessage":"Cannot reset","messagePattern":"Cannot reset","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"Android/dokit/src/main/java/com/didichuxing/doraemonkit/picasso/MarkableInputStream.java","lineNumber":99,"sourceCode":"      } else {\n        reset = offset;\n        in.mark((int) (limit - offset));\n      }\n      this.limit = limit;\n    } catch (IOException e) {\n      throw new IllegalStateException(\"Unable to mark: \" + e);\n    }\n  }\n\n  /** Resets the stream to the most recent {@link #mark mark}. */\n  @Override public void reset() throws IOException {\n    reset(defaultMark);\n  }\n\n  /** Resets the stream to the position recorded by {@code token}. */\n  public void reset(long token) throws IOException {\n    if (offset > limit || token < reset) {\n      throw new IOException(\"Cannot reset\");\n    }\n    in.reset();\n    skip(reset, token);\n    offset = token;\n  }\n\n  /** Skips {@code target - current} bytes and returns. */\n  private void skip(long current, long target) throws IOException {\n    while (current < target) {\n      long skipped = in.skip(target - current);\n      if (skipped == 0) {\n        if (read() == -1) {\n          break; // EOF\n        } else {\n          skipped = 1;\n        }\n      }\n      current += skipped;","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/didi/DoKit/blob/626827cddb2feb2f3aee87a52a064b4e5ca2bed4/Android/dokit/src/main/java/com/didichuxing/doraemonkit/picasso/MarkableInputStream.java#L81-L117","documentation":"MarkableInputStream.reset(token) throws IOException(\"Cannot reset\") when the current offset has passed the marked limit or the requested token is before the earliest valid reset point. The mark window is bounded by the readlimit given to the underlying stream, so once data has been read past it, rewinding is impossible.","triggerScenarios":"Calling reset(token) after reading more bytes than the saved mark window covers, or resetting to a token older than the current reset floor (e.g. resetting twice to progressively earlier positions).","commonSituations":"Decoding logic that inspects many bytes (multiple bounds decodes, EXIF scanning) before rewinding; reused streams where reset is attempted after a previous reset shrank the window.","solutions":["Reset promptly after reading, before consuming bytes beyond the mark window","Increase the mark limit / buffer the stream fully when large rewinds are needed","Catch the IOException at the decode layer and retry by re-requesting the stream from the Downloader"],"exampleFix":"// before\nlong token = stream.savePosition(0);\ninspectManyBytes(stream);\nstream.reset(token); // IOException: Cannot reset\n\n// after\nlong token = stream.savePosition(0);\ninspectFewBytes(stream);\nstream.reset(token);\n// or re-open the stream and start a fresh decode on IOException","handlingStrategy":"try-catch","validationCode":"// structural prevention only: read little before reset, or buffer fully\nif (!streamIsBig && bytesToInspect > MARK_WINDOW) { stream = new BufferedInputStream(stream, bytesToInspect); }","typeGuard":null,"tryCatchPattern":"try { stream.reset(token); } catch (IOException e) { if (\"Cannot reset\".equals(e.getMessage())) { stream = reopenStream(); /* fresh decode */ } else { throw e; } }","preventionTips":["Reset soon after marking; do not read past the mark window before rewinding","Buffer the stream when large inspections precede the real decode","On IOException from reset, prefer re-opening the stream over forcing the rewind"],"tags":["android","picasso","stream","io","decode"],"backgroundTag":null,"analyzedSha":"626827cddb2feb2f3aee87a52a064b4e5ca2bed4","analyzedAt":"2026-08-14T12:45:58.758Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}