jhy/jsoup · error · IOException
Resetting to invalid mark
Error message
Resetting to invalid mark
What it means
SimpleBufferedInput.rewindToMark is the internal buffer primitive backing mark/reset. It throws IOException('Resetting to invalid mark') when bufMark < 0, meaning setMark() was never called or clearMark() already ran. It enforces the standard Java InputStream mark/reset contract at the buffer level.
Solutions
- Ensure each reset() has a preceding setMark()/mark() that hasn't been cleared or consumed.
- Guard: only rewind when a mark is active (track it in your wrapper).
- Treat mark as single-use: clear your 'marked' flag after reset.
- If you need repeated rewinds, buffer the region in a byte[] yourself.
- Upgrade jsoup if the invalid mark arises from internal detection flows (fixed in later versions).
Example fix
// before buf.setMark(); buf.clearMark(); buf.rewindToMark(); // throws // after buf.setMark(); buf.rewindToMark(); buf.clearMark();
Defensive patterns
Strategy: validation
Validate before calling
if (!markActive) throw new IllegalStateException("no active mark; cannot rewind"); Try / catch
try { rewind(); } catch (IOException e) { if (e.getMessage().contains("invalid mark")) { /* re-buffer region and retry */ } } Prevention
- Track mark lifecycle explicitly: set -> use -> clear, never rewind after clear.
- Treat marks as single-shot, not reusable.
- Buffer bytes you need to re-read instead of relying on mark/reset.
- Prefer BufferedInputStream with a known buffer size for your own mark/reset needs.
When it happens
Trigger: ControllableInputStream.reset() delegating to a buffer whose mark was cleared (e.g. after prior reset or after jsoup's charset detection completes); calling reset() with no matching outstanding mark(); double-reset on the same mark.
Common situations: Custom stream wrappers around jsoup internals; code paths that call reset() twice; reading after jsoup's detection code cleared the mark, then attempting a rewind.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
AI-assisted analysis of jhy/jsoup@9851ac5d9c (2026-09-08).
Data as JSON: /api/errors/0a55618e400c6bbb.
Report an issue: GitHub.
Appendix: source
Thrown at src/main/java/org/jsoup/internal/SimpleBufferedInput.java:173
if (bufPos < bufLength || lookahead >= 0) return true;
if (inReadFully) return false;
int next = in.read();
if (next == -1) {
inReadFully = true;
return false;
}
lookahead = next;
return true;
}
void setMark() {
bufMark = bufPos;
}
void rewindToMark() throws IOException {
if (bufMark < 0)
throw new IOException("Resetting to invalid mark");
bufPos = bufMark;
}
void clearMark() {
bufMark = -1;
}
private void compact() {
if (byteBuf == null || bufPos == 0) return;
int keepFrom = bufMark >= 0 ? bufMark : bufPos;
if (keepFrom <= 0) return;
int remaining = bufLength - keepFrom;
if (remaining > 0) {
System.arraycopy(byteBuf, keepFrom, byteBuf, 0, remaining);
}
bufLength = remaining;
bufPos -= keepFrom;View on GitHub (pinned to 9851ac5d9c)