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

  1. Ensure each reset() has a preceding setMark()/mark() that hasn't been cleared or consumed.
  2. Guard: only rewind when a mark is active (track it in your wrapper).
  3. Treat mark as single-use: clear your 'marked' flag after reset.
  4. If you need repeated rewinds, buffer the region in a byte[] yourself.
  5. 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

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)