jhy/jsoup · error · UncheckedIOException
WTF: No buffer left to unconsume.
Error message
WTF: No buffer left to unconsume.
What it means
Internal invariant failure in CharacterReader.unconsume: it tries to move bufPos below 0, meaning more characters were unconsumed than consumed. Per its contract, unconsume() is only valid immediately after a consume() with no intervening bufferUp(); the message signals a parser bug rather than bad user input. It is reached when a caller (e.g. recursive unconsume chains or lookbehind helpers like matchesDigit) over-rewinds the buffer.
Solutions
- Ensure unconsume() is called only directly after a consume(), with no bufferUp() or other reads in between.
- Fix lookbehind logic to track how many characters were actually consumed before rewinding; never call unconsume() more times than consume().
- Replace fragile consume/unconsume pairs with reader.match(...) lookahead APIs, which do not mutate position on failure.
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at src/main/java/org/jsoup/parser/CharacterReader.java:286 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of jhy/jsoup@9851ac5d9c (2026-09-08).
Data as JSON: /api/errors/1a3219ed7b39da5a.
Report an issue: GitHub.
Appendix: source
Thrown at src/main/java/org/jsoup/parser/CharacterReader.java:286
}
/**
Consume one character off the queue.
@return first character on queue, or EOF if the queue is empty.
*/
public char consume() {
bufferUp();
char val = isEmptyNoBufferUp() ? EOF : charBuf[bufPos];
bufPos++;
return val;
}
/**
Unconsume one character (bufPos--). MUST only be called directly after a consume(), and no chance of a bufferUp.
*/
void unconsume() {
if (bufPos < 1)
throw new UncheckedIOException(new IOException("WTF: No buffer left to unconsume.")); // a bug if this fires, need to trace it.
bufPos--;
}
/**
* Moves the current position by one.
*/
public void advance() {
bufPos++;
}
/**
* Returns the number of characters between the current position and the next instance of the input char
* @param c scan target
* @return offset between current position and next instance of target. -1 if not found.
*/
int nextIndexOf(char c) {
// doesn't handle scanning for surrogatesView on GitHub (pinned to 9851ac5d9c)