{"record":{"id":"72a66dd09c73937e","repo":"didi/DoKit","slug":"unable-to-mark-e","errorCode":null,"errorMessage":"Unable to mark: <e>","messagePattern":"Unable to mark: <e>","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"Android/dokit/src/main/java/com/didichuxing/doraemonkit/picasso/MarkableInputStream.java","lineNumber":87,"sourceCode":"   * {@code reset} thru {@code limit}. Since we can't call {@code mark()}\n   * without also adjusting the reset-to-position on the underlying stream this\n   * method resets first and then marks the union of the two byte ranges. On\n   * buffered streams this additional cursor motion shouldn't result in any\n   * additional I/O.\n   */\n  private void setLimit(long limit) {\n    try {\n      if (reset < offset && offset <= this.limit) {\n        in.reset();\n        in.mark((int) (limit - reset));\n        skip(reset, offset);\n      } 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","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/didi/DoKit/blob/626827cddb2feb2f3aee87a52a064b4e5ca2bed4/Android/dokit/src/main/java/com/didichuxing/doraemonkit/picasso/MarkableInputStream.java#L69-L105","documentation":"MarkableInputStream.setLimit() throws IllegalStateException when the wrapped stream's mark()/reset() calls fail with an IOException. Picasso wraps network streams in MarkableInputStream so the same bytes can be re-read (e.g. to decode bounds first); when the underlying stream cannot honor marking, this wrapping error surfaces as a runtime exception.","triggerScenarios":"The wrapped InputStream throwing IOException inside markSupported-limited streams, or reset() failing after the mark window expired, during savePosition(0)/setLimit operations in the stream decode path.","commonSituations":"Custom Downloaders returning streams with small or broken mark limits; streams already consumed before being wrapped; okhttp response streams after the connection was closed or intercepted oddly.","solutions":["Return a stream that supports marking with a sufficient readlimit from the Downloader (e.g. wrap in BufferedInputStream)","Ensure the stream is fresh (not already consumed) when handed to Picasso","If wrapping an exotic stream, subclass or pre-buffer it so mark/reset work"],"exampleFix":"// before\nreturn new Response(rawSocketStream, false, len); // mark/reset fail downstream\n\n// after\nreturn new Response(new BufferedInputStream(rawSocketStream, 64 * 1024), false, len);","handlingStrategy":"fallback","validationCode":"InputStream wrapped = rawStream.markSupported() ? rawStream : new BufferedInputStream(rawStream, 64 * 1024);\nreturn new Response(wrapped, false, len);","typeGuard":"boolean isMarkSafe(InputStream in) { return in.markSupported(); }","tryCatchPattern":"try { return new Response(raw, false, len); } catch (IllegalStateException e) { if (e.getMessage() != null && e.getMessage().contains(\"Unable to mark\")) { return new Response(new BufferedInputStream(raw, 64 * 1024), false, len); } throw e; }","preventionTips":["Always hand Picasso buffered streams that support mark/reset","Do not consume the stream before returning it from a Downloader"],"tags":["android","picasso","stream","network","io"],"backgroundTag":null,"analyzedSha":"626827cddb2feb2f3aee87a52a064b4e5ca2bed4","analyzedAt":"2026-08-14T12:45:58.758Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}