iBotPeaches/Apktool · error · IOException

Mark not set

Error message

Mark not set

What it means

BinaryDataInputStream.reset() throws when reset() is called without a preceding mark(): the internal mark field still holds its -1 sentinel. The underlying stream may support marks, but this wrapper has no recorded position to rewind to.

Solutions

  1. Guarantee mark() is called on every path that can later reach reset() (mark at loop/function entry)
  2. Track a boolean or check the mark state before reset()
  3. Restructure so mark and reset stay in the same scope/try block
  4. Add a unit test covering the branch that previously skipped mark()

Example fix

// before
if (needsLookahead) { in.mark(64); peek(); }
in.reset(); // Mark not set when needsLookahead was false

// after
in.mark(64);
if (needsLookahead) { peek(); in.reset(); } else { /* no rewind needed */ }
Defensive patterns

Strategy: validation

Validate before calling

// Pair every reachable reset() with a guaranteed mark()
final boolean[] marked = { false };
Runnable mark = () -> { in.mark(BUF); marked[0] = true; };
// before reset:
if (!marked[0]) throw new IllegalStateException("reset() without mark() on this path");
in.reset();

Try / catch

try {
    in.reset();
} catch (IOException e) {
    if ("Mark not set".equals(e.getMessage())) {
        // control-flow bug: mark was skipped on this path — fix the caller, do not retry
        throw new IllegalStateException("Parser bug: reset reached without mark", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling reset() before any mark() on the same stream instance, or after logic paths where mark() was skipped (conditional parsing branches that only mark sometimes).

Common situations: Parser control flow where one branch marks and rewinds while another reaches the same reset() call without marking; refactors that move mark() into a guard block; copy-paste of reset() into a second parse loop sharing the stream.

Related errors


AI-assisted analysis of iBotPeaches/Apktool@79b63384d7 (2026-08-14). Data as JSON: /api/errors/2aa2ba79a9fec803. Report an issue: GitHub.

Appendix: source

Thrown at brut.j.util/src/main/java/brut/util/BinaryDataInputStream.java:345

    @Override
    public int available() throws IOException {
        return (int) Math.min(in.available(), remaining());
    }

    @Override
    public synchronized void mark(int readlimit) {
        // We can't throw an exception here, so mark even if mark isn't supported, since reset won't work anyway.
        in.mark(readlimit);
        mMark = mPosition;
    }

    @Override
    public synchronized void reset() throws IOException {
        if (!markSupported()) {
            throw new IOException("Mark not supported");
        }
        if (mMark == -1) {
            throw new IOException("Mark not set");
        }
        in.reset();
        mPosition = mMark;
    }
}

View on GitHub (pinned to 79b63384d7)