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.
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)
Solutions
- Guarantee mark() is called on every path that can later reach reset() (mark at loop/function entry)
- Track a boolean or check the mark state before reset()
- Restructure so mark and reset stay in the same scope/try block
- 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
- Call mark() at function/loop entry so every path has a mark
- Keep mark and reset in the same try block or scope
- Unit-test all branches of conditional-lookahead parsing
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
- Mark not supported
- Illegal backwards jump from %s to %s
- Jump failed: skipped %s bytes (expected: %s)
- Path is null or empty
- Absolute paths are not allowed
AI-assisted analysis of iBotPeaches/Apktool@79b63384d7 (2026-08-14).
Data as JSON: /api/errors/2aa2ba79a9fec803.
Report an issue: GitHub.