{"record":{"id":"f9f753cd2f8095c1","repo":"stride3d/stride","slug":"cannot-unget-another-character","errorCode":null,"errorMessage":"Cannot unget another character!","messagePattern":"Cannot unget another character!","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"sources/shaders/Stride.Shaders.Parsers/CppNet/LexerSource.cs","lineNumber":265,"sourceCode":"\t\t\tif (isLineSeparator(c)) {\n\t\t\t\tline--;\n\t\t\t\tcolumn = lastcolumn;\n\t\t\t\tcr = false;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tcolumn--;\n\t\t\t}\n\t\t\tswitch (ucount) {\n\t\t\t\tcase 0:\n\t\t\t\t\tu0 = c;\n\t\t\t\t\tucount = 1;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 1:\n\t\t\t\t\tu1 = c;\n\t\t\t\t\tucount = 2;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tthrow new InvalidOperationException(\n\t\t\t\t\t\t\t\"Cannot unget another character!\"\n\t\t\t\t\t\t\t\t);\n\t\t\t}\n\t\t\t// reader.unread(c);\n\t\t}\n\t}\n\n\t/* Consumes the rest of the current line into an invalid. */\n\tprivate Token invalid(StringBuilder text, String reason) {\n\t\tint\td = read();\n\t\twhile (!isLineSeparator(d)) {\n\t\t\ttext.Append((char)d);\n\t\t\td = read();\n\t\t}\n\t\tunread(d);\n        return new Token(Token.INVALID, text.ToString(), reason);\n\t}\n","sourceCodeStart":247,"sourceCodeEnd":283,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/shaders/Stride.Shaders.Parsers/CppNet/LexerSource.cs#L247-L283","documentation":"LexerSource.unread() can push back at most two characters (u0, u1); when a third consecutive unread is requested, this InvalidOperationException is thrown. It is an internal lexer invariant: the token scanners (invalid, cppcomment, escape, character, String, _number) must never need to push back more than two characters.","triggerScenarios":"A lexer scan path (e.g. _number, escape, string/char literal scanning, comment handling) calls unread() three times without an intervening read(), exhausting the two-slot pushback buffer — typically caused by malformed input that breaks the scanner's assumptions about look-ahead depth.","commonSituations":"Malformed numeric literals or escape sequences in shader source (e.g. '1.2.3', dangling backslash in a string); corrupted or truncated input streams; bugs in custom token scanners that over-look-ahead.","solutions":["Fix the malformed token in the shader source (check numbers, escape sequences, and string/char literals around the reported line/column).","Verify the input stream is complete and not corrupted/truncated mid-token.","Install a PreprocessorListener so upstream invalid-token paths report errors instead of cascading.","If you extended the lexer, increase the pushback buffer (add u2/ucount case 2) or reduce look-ahead to at most two chars."],"exampleFix":"// before: scanner unreads three times in a row\nunread(d); unread(e); unread(f); // throws: Cannot unget another character!\n// after: re-read instead of a third unget\nunread(d); unread(e);\nf = read(); // keep pushback depth <= 2","handlingStrategy":"validation","validationCode":"// Lint shader source for tokens that break look-ahead assumptions\nforeach (var m in Regex.Matches(src, @\"\\d+\\.\\d*\\.|\\\\[^n'\\\"\\\\]|\\z\\\\\"))\n    report(\"Malformed numeric/escape sequence near \" + m.Index);","typeGuard":null,"tryCatchPattern":"try\n{\n    var token = lexerSource.token();\n}\ncatch (InvalidOperationException ex) when (ex.Message == \"Cannot unget another character!\")\n{\n    report(line, column, \"Malformed token pushed lexer look-ahead past 2 characters\");\n}","preventionTips":["Validate numeric literals, escapes, and string/char literals in shader sources before preprocessing.","Never leave custom scanners with more than 2 characters of push-back.","Keep input streams intact — truncated files can strand the scanner mid-token."],"tags":["lexer","pushback-overflow","internal-error"],"backgroundTag":"internal-invariant-violation","analyzedSha":"96fad776d210c221682aac1ccdf4c79dc046fc38","analyzedAt":"2026-09-14T02:59:31.279Z","contentChangedAt":"2026-09-14T02:59:31.279Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}