{"record":{"id":"ec754a17802d6c10","repo":"antlr/antlr4","slug":"cannot-seek-to-negative-index-index-ec754a","errorCode":null,"errorMessage":"cannot seek to negative index ${index}","messagePattern":"cannot seek to negative index (.+?)","errorType":"exception","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"runtime/CSharp/src/UnbufferedCharStream.cs","lineNumber":385,"sourceCode":"        /// <c>index-bufferStartIndex</c>\n        /// .\n        /// </remarks>\n        public virtual void Seek(int index)\n        {\n            if (index == currentCharIndex)\n            {\n                return;\n            }\n            if (index > currentCharIndex)\n            {\n                Sync(index - currentCharIndex);\n                index = Math.Min(index, BufferStartIndex + n - 1);\n            }\n            // index == to bufferStartIndex should set p to 0\n            int i = index - BufferStartIndex;\n            if (i < 0)\n            {\n                throw new ArgumentException(\"cannot seek to negative index \" + index);\n            }\n            else\n            {\n                if (i >= n)\n                {\n                    throw new NotSupportedException(\"seek to index outside buffer: \" + index + \" not in \" + BufferStartIndex + \"..\" + (BufferStartIndex + n));\n                }\n            }\n            p = i;\n            currentCharIndex = index;\n            if (p == 0)\n            {\n                lastChar = lastCharBufferStart;\n            }\n            else\n            {\n                lastChar = data[p - 1];\n            }","sourceCodeStart":367,"sourceCodeEnd":403,"githubUrl":"https://github.com/antlr/antlr4/blob/7d5770395bb7b02eb56e7c62662cb1d7c08f42a3/runtime/CSharp/src/UnbufferedCharStream.cs#L367-L403","documentation":"UnbufferedCharStream keeps only a rolling window of already unread data. Seek() maps the requested absolute index to a buffer offset; a negative offset means the position was discarded and cannot be restored. Backward seeks are therefore illegal once the needed data has left the buffer.","triggerScenarios":"Calling Seek(oldIndex) after consuming beyond the retained window; releasing the marker that protected old data and then seeking back; or custom code assuming ICharStream supports arbitrary random access.","commonSituations":"Backtracking or reset logic is used with an unbuffered stream; a parser strategy requires rewinding; or migration from AntlrInputStream/CommonTokenStream to UnbufferedCharStream without preserving markers.","solutions":["Do not seek backward on UnbufferedCharStream; process input in one forward pass.","Keep a Mark() active for any earlier position that must remain seekable and release it only after seeking is complete.","Use AntlrInputStream (fully buffered characters) when backward seeks are required."],"exampleFix":"// before\nvar chars = new UnbufferedCharStream(reader, 4096);\nchars.Seek(0);\n\n// after\nvar chars = new AntlrInputStream(reader); // supports random access\nchars.Seek(0);","handlingStrategy":"validation","validationCode":"if (targetIndex < chars.Index && randomAccessRequired)\n    chars = new AntlrInputStream(reader); // or retain a Mark()","typeGuard":"static bool SupportsBackwardSeek(ICharStream stream) => stream is not UnbufferedCharStream;","tryCatchPattern":"try { chars.Seek(index); }\ncatch (ArgumentException ex) when (ex.Message.StartsWith(\"cannot seek to negative index\")) { /* use buffered stream or keep markers */ }","preventionTips":["Treat UnbufferedCharStream as forward-only unless markers protect old data.","Use AntlrInputStream for backtracking/reset workloads.","Keep Mark() active across any backward seeks."],"tags":["antlr","csharp","char-stream","seek","streaming"],"backgroundTag":null,"analyzedSha":"7d5770395bb7b02eb56e7c62662cb1d7c08f42a3","analyzedAt":"2026-08-14T14:47:56.354Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}