apache/dubbo · error · IOException
Mark buffer is full!
Error message
Mark buffer is full!
What it means
Thrown by the mark-buffering InputStream created in StreamUtils.markSupportedInputStream(InputStream, int markBufferSize) when, after mark() has been set, the number of bytes read exceeds markBufferSize. This wrapper is used only when the underlying stream does not support mark/reset itself; it buffers bytes between mark() and reset() in a fixed-size array, and once mPosition reaches markBufferSize it raises IOException("Mark buffer is full!"). It is the direct analogue of java.io's 'Read ahead limit exceeded' but with an explicit, user-controlled cap.
Source
Thrown at dubbo-common/src/main/java/org/apache/dubbo/common/io/StreamUtils.java:155
public int read() throws IOException {
if (!mInMarked) {
return is.read();
} else {
if (mPosition < mCount) {
byte b = mMarkBuffer[mPosition++];
return b & 0xFF;
}
if (!mInReset) {
if (mDry) {
return -1;
}
if (null == mMarkBuffer) {
mMarkBuffer = new byte[markBufferSize];
}
if (mPosition >= markBufferSize) {
throw new IOException("Mark buffer is full!");
}
int read = is.read();
if (-1 == read) {
mDry = true;
return -1;
}
mMarkBuffer[mPosition++] = (byte) read;
mCount++;
return read;
} else {
// mark buffer is used, exit mark status!
mInMarked = false;
mInReset = false;
mPosition = 0;
mCount = 0;View on GitHub (pinned to 3a3043227f)
Solutions
- Size markBufferSize to the maximum bytes you will read between mark() and reset(), e.g. markSupportedInputStream(in, requiredLookahead).
- Call reset() as early as possible once the decision point is passed, to free the buffer.
- Avoid wrapping already-markable streams (the wrapper returns the original unchanged when markSupported() is true).
Example fix
// before InputStream m = StreamUtils.markSupportedInputStream(in); // default cap too small m.mark(64); // ... read 4KB to inspect ... m.reset(); // -> IOException: Mark buffer is full! // after InputStream m = StreamUtils.markSupportedInputStream(in, 8 * 1024); // cap >= lookahead m.mark(64); // ... read up to 8KB ... m.reset();
Defensive patterns
Strategy: validation
Validate before calling
int requiredLookahead = 8 * 1024; // >= max bytes between mark() and reset() InputStream m = StreamUtils.markSupportedInputStream(in, requiredLookahead); m.mark(requiredLookahead);
Type guard
static int markBufferSizeFor(int maxReadAhead) {
return Math.max(maxReadAhead, 1);
} Try / catch
try {
m.reset();
} catch (IOException e) {
// buffer too small — re-open the source and re-read instead of replaying
in = reopen(); m = StreamUtils.markSupportedInputStream(in, biggerCap);
} Prevention
- Choose markBufferSize >= the maximum bytes you will read before reset().
- Note mark(readlimit) is ignored — only the constructor cap matters.
- Call reset() as soon as the decision point passes to free the buffer.
- Don't wrap streams that already support mark — the wrapper returns them unchanged.
When it happens
Trigger: Wrapping a non-markable stream with markSupportedInputStream(in, N), calling mark(N), then reading more than N bytes before reset(); or calling the single-arg markSupportedInputStream(in) whose default buffer is too small for the replay window.
Common situations: Replaying HTTP response bodies, multipart parsing, or protocol peeking where the consumed-ahead bytes exceed the configured buffer; default buffer size used for a stream that needs a large lookahead; mark(readlimit) ignored — this implementation ignores readlimit and uses the constructor cap.
Related errors
- should mark before reset!
- Negative initial size: ${size}
- Read-ahead limit < 0
- Stream closed
- Negative buffer size
AI-assisted analysis of apache/dubbo@3a3043227f (2026-08-14).
Data as JSON: /api/errors/d73a2e8f0b55376e.
Report an issue: GitHub.