antlr/antlr4 · error · InvalidOperationException
release() called with an invalid marker.
Error message
release() called with an invalid marker.
What it means
UnbufferedCharStream markers form a stack: Mark() returns -numMarkers after incrementing it, and Release(marker) accepts only the currently innermost marker. Any other value, duplicate release, or foreign marker is rejected. This preserves the exact buffer window that still-active markers require.
Source
Thrown at runtime/CSharp/src/UnbufferedCharStream.cs:334
{
if (numMarkers == 0)
{
lastCharBufferStart = lastChar;
}
int mark = -numMarkers - 1;
numMarkers++;
return mark;
}
/// <summary>Decrement number of markers, resetting buffer if we hit 0.</summary>
/// <remarks>Decrement number of markers, resetting buffer if we hit 0.</remarks>
/// <param name="marker"/>
public virtual void Release(int marker)
{
int expectedMark = -numMarkers;
if (marker != expectedMark)
{
throw new InvalidOperationException("release() called with an invalid marker.");
}
numMarkers--;
if (numMarkers == 0 && p > 0)
{
// release buffer when we can, but don't do unnecessary work
// Copy data[p]..data[n-1] to data[0]..data[(n-1)-p], reset ptrs
// p is last valid char; move nothing if p==n as we have no valid char
System.Array.Copy(data, p, data, 0, n - p);
// shift n-p char from p to 0
n = n - p;
p = 0;
lastCharBufferStart = lastChar;
}
}
public virtual int Index
{
getView on GitHub (pinned to 7d5770395b)
Solutions
- Store the value returned by Mark() and pass exactly that value to Release().
- Release markers in reverse acquisition order.
- Wrap marked regions in try/finally so every marker is released exactly once.
- Do not share or reuse markers across streams.
Example fix
// before
int m = stream.Mark();
stream.Release(m);
stream.Release(m);
// after
int m = stream.Mark();
try { /* work */ }
finally { stream.Release(m); } Defensive patterns
Strategy: validation
Validate before calling
var markerStack = new Stack<int>();
markerStack.Push(stream.Mark());
// before releasing:
if (markerStack.Peek() == marker) { stream.Release(markerStack.Pop()); } Try / catch
try { stream.Release(marker); }
catch (InvalidOperationException ex) when (ex.Message.Contains("invalid marker")) { /* release remaining markers in LIFO order */ } Prevention
- Always pair Mark() with exactly one Release() in try/finally.
- Release markers in reverse order.
- Never cache or reuse marker values across streams.
When it happens
Trigger: Calling Release() twice with the same marker; releasing markers out of LIFO order; passing a marker from a different stream; or passing 0/1 instead of the value returned by Mark().
Common situations: Custom parser harnesses that cache markers; exception paths that skip a release and later release the wrong marker; or code ported from an API where markers are opaque IDs that can be released in any order.
Related errors
- cannot consume EOF
- cannot seek to negative index ${index}
- seek to index outside buffer: ${index} not in ${bufferStartI
- Unbuffered stream cannot know its size
- interval ${interval} outside buffer: ${bufferStartIndex}..${
AI-assisted analysis of antlr/antlr4@7d5770395b (2026-08-14).
Data as JSON: /api/errors/c69db6fc35290e35.
Report an issue: GitHub.