{"record":{"id":"d5a30629f7f85bd8","repo":"antlr/antlr4","slug":"release-called-with-an-invalid-marker-d5a306","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/UnbufferedTokenStream.cs","lineNumber":298,"sourceCode":"        /// is called in the wrong order.</p>\n        /// </remarks>\n        public virtual int Mark()\n        {\n            if (numMarkers == 0)\n            {\n                lastTokenBufferStart = lastToken;\n            }\n            int mark = -numMarkers - 1;\n            numMarkers++;\n            return mark;\n        }\n\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)\n            {\n                // can we release buffer?\n                if (p > 0)\n                {\n                    // Copy tokens[p]..tokens[n-1] to tokens[0]..tokens[(n-1)-p], reset ptrs\n                    // p is last valid token; move nothing if p==n as we have no valid char\n                    System.Array.Copy(tokens, p, tokens, 0, n - p);\n                    // shift n-p tokens from p to 0\n                    n = n - p;\n                    p = 0;\n                }\n                lastTokenBufferStart = lastToken;\n            }\n        }\n","sourceCodeStart":280,"sourceCodeEnd":316,"githubUrl":"https://github.com/antlr/antlr4/blob/7d5770395bb7b02eb56e7c62662cb1d7c08f42a3/runtime/CSharp/src/UnbufferedTokenStream.cs#L280-L316","documentation":"UnbufferedTokenStream implements Mark()/Release() as a strictly LIFO stack: Mark() returns -numMarkers-1 (so marks are -1, -2, ...) and Release(marker) requires marker == -numMarkers. The thrown InvalidOperationException means the marker passed to Release() is not the most recently acquired, still-open mark. The stream cannot release out of order because each mark pins a buffer window.","triggerScenarios":"Calling Release(m) twice for the same m; calling Release(0) (marks are negative, never 0); releasing marks in a non-LIFO order (release mark -2 while -1 is still open); releasing a marker obtained from a different token stream or after an exception path already released it; calling Release() when numMarkers==0.","commonSituations":"Manually managing mark/release around backtracking without try/finally, so an early exception or return skips one release and shifts the stack; mixing markers between a CommonTokenStream and an UnbufferedTokenStream; porting code that assumed Java's index-style markers (0,1,2...) instead of ANTLR 4's negative stack markers.","solutions":["Release markers in strict reverse order of acquisition (last Mark() released first) and never release the same marker twice","Wrap usage in try/finally so the matching Release() always runs even when parsing throws","Check the invariant before calling: marker must equal the value returned by the most recent, unreleased Mark() (i.e. -(numMarkers))","If you only need a single checkpoint, keep exactly one mark at a time: Mark() once, Release() once, repeat"],"exampleFix":"// before\nint m = stream.Mark();\nParse();\nstream.Release(m);\nstream.Release(m); // second release throws\n\n// after\nint m = stream.Mark();\ntry {\n    Parse();\n}\nfinally {\n    stream.Release(m); // exactly once, in LIFO order\n}","handlingStrategy":"validation","validationCode":"// C#: release only the top-of-stack mark; marks are -1, -2, ... in LIFO order\n// track open marks yourself when nesting\nvar openMarks = new Stack<int>();\nint m = stream.Mark();\nopenMarks.Push(m);\nif (openMarks.Peek() == m) { stream.Release(openMarks.Pop()); } // valid only when m == last Mark() unreleased","typeGuard":null,"tryCatchPattern":"try { int m = stream.Mark(); try { Parse(stream); } finally { stream.Release(m); } } catch (InvalidOperationException ex) when (ex.Message.Contains(\"invalid marker\")) { // mark stack corrupted earlier: reset the stream and restart parse from a fresh UnbufferedTokenStream }","preventionTips":["Always pair Mark/Release with try/finally so no release is skipped or duplicated","Never assume marker values: use only what Mark() returned, and release in reverse acquisition order","Keep exactly one open mark at a time unless your nesting logic is explicitly LIFO-tested"],"tags":["antlr","csharp","token-stream","markers","lifo"],"backgroundTag":null,"analyzedSha":"7d5770395bb7b02eb56e7c62662cb1d7c08f42a3","analyzedAt":"2026-08-14T14:47:56.354Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}