{"record":{"id":"c69db6fc35290e35","repo":"antlr/antlr4","slug":"release-called-with-an-invalid-marker-c69db6","errorCode":null,"errorMessage":"release() called with an invalid marker.","messagePattern":"release\\(\\) called with an invalid marker\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"runtime/CSharp/src/UnbufferedCharStream.cs","lineNumber":334,"sourceCode":"        {\n            if (numMarkers == 0)\n            {\n                lastCharBufferStart = lastChar;\n            }\n            int mark = -numMarkers - 1;\n            numMarkers++;\n            return mark;\n        }\n\n        /// <summary>Decrement number of markers, resetting buffer if we hit 0.</summary>\n        /// <remarks>Decrement number of markers, resetting buffer if we hit 0.</remarks>\n        /// <param name=\"marker\"/>\n        public virtual void Release(int marker)\n        {\n            int expectedMark = -numMarkers;\n            if (marker != expectedMark)\n            {\n                throw new InvalidOperationException(\"release() called with an invalid marker.\");\n            }\n            numMarkers--;\n            if (numMarkers == 0 && p > 0)\n            {\n                // release buffer when we can, but don't do unnecessary work\n                // Copy data[p]..data[n-1] to data[0]..data[(n-1)-p], reset ptrs\n                // p is last valid char; move nothing if p==n as we have no valid char\n                System.Array.Copy(data, p, data, 0, n - p);\n                // shift n-p char from p to 0\n                n = n - p;\n                p = 0;\n                lastCharBufferStart = lastChar;\n            }\n        }\n\n        public virtual int Index\n        {\n            get","sourceCodeStart":316,"sourceCodeEnd":352,"githubUrl":"https://github.com/antlr/antlr4/blob/7d5770395bb7b02eb56e7c62662cb1d7c08f42a3/runtime/CSharp/src/UnbufferedCharStream.cs#L316-L352","documentation":"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.","triggerScenarios":"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().","commonSituations":"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.","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."],"exampleFix":"// before\nint m = stream.Mark();\nstream.Release(m);\nstream.Release(m);\n\n// after\nint m = stream.Mark();\ntry { /* work */ }\nfinally { stream.Release(m); }","handlingStrategy":"validation","validationCode":"var markerStack = new Stack<int>();\nmarkerStack.Push(stream.Mark());\n// before releasing:\nif (markerStack.Peek() == marker) { stream.Release(markerStack.Pop()); }","typeGuard":null,"tryCatchPattern":"try { stream.Release(marker); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"invalid marker\")) { /* release remaining markers in LIFO order */ }","preventionTips":["Always pair Mark() with exactly one Release() in try/finally.","Release markers in reverse order.","Never cache or reuse marker values across streams."],"tags":["antlr","csharp","char-stream","markers","streaming"],"backgroundTag":null,"analyzedSha":"7d5770395bb7b02eb56e7c62662cb1d7c08f42a3","analyzedAt":"2026-08-14T14:47:56.354Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}