{"record":{"id":"d764a9d6525b8191","repo":"ssssssss-team/spider-flow","slug":"no-more-characters-in-stream","errorCode":null,"errorMessage":"No more characters in stream.","messagePattern":"No more characters in stream\\.","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"spider-flow-core/src/main/java/org/spiderflow/core/expression/parsing/CharacterStream.java","lineNumber":37,"sourceCode":"\tpublic CharacterStream (String source, int start, int end) {\n\t\tif (start > end) throw new IllegalArgumentException(\"Start must be <= end.\");\n\t\tif (start < 0) throw new IndexOutOfBoundsException(\"Start must be >= 0.\");\n\t\tif (start > Math.max(0, source.length() - 1)) throw new IndexOutOfBoundsException(\"Start outside of string.\");\n\t\tif (end > source.length()) throw new IndexOutOfBoundsException(\"End outside of string.\");\n\n\t\tthis.source = source;\n\t\tthis.index = start;\n\t\tthis.end = end;\n\t}\n\n\t/** Returns whether there are more characters in the stream **/\n\tpublic boolean hasMore () {\n\t\treturn index < end;\n\t}\n\n\t/** Returns the next character without advancing the stream **/\n\tpublic char peek () {\n\t\tif (!hasMore()) throw new RuntimeException(\"No more characters in stream.\");\n\t\treturn source.charAt(index++);\n\t}\n\n\t/** Returns the next character and advance the stream **/\n\tpublic char consume () {\n\t\tif (!hasMore()) throw new RuntimeException(\"No more characters in stream.\");\n\t\treturn source.charAt(index++);\n\t}\n\n\t/** Matches the given needle with the next characters. Returns true if the needle is matched, false otherwise. If there's a\n\t * match and consume is true, the stream is advanced by the needle's length. */\n\tpublic boolean match (String needle, boolean consume) {\n\t\tint needleLength = needle.length();\n\t\tif(needleLength + index >end){\n\t\t\treturn false;\n\t\t}\n\t\tfor (int i = 0, j = index; i < needleLength; i++, j++) {\n\t\t\tif (index >= end) return false;","sourceCodeStart":19,"sourceCodeEnd":55,"githubUrl":"https://github.com/ssssssss-team/spider-flow/blob/c799cca99c7d064673dc79e6ef06a08bbc0e292d/spider-flow-core/src/main/java/org/spiderflow/core/expression/parsing/CharacterStream.java#L19-L55","documentation":"CharacterStream.peek() returns the next character without advancing (despite the post-increment in the shown code, the Javadoc contract is non-advancing lookahead). If the stream is exhausted (index >= end), it throws RuntimeException(\"No more characters in stream.\").","triggerScenarios":"Calling peek() when hasMore() is false — i.e. after consuming all characters of the expression source.","commonSituations":"Tokenizers peeking at the next character inside loops without checking hasMore() first, especially at end-of-input for expressions ending exactly at a token boundary.","solutions":["Always call hasMore() before peek()","Restructure the parsing loop to terminate when !hasMore() instead of catching the exception","Use match(needle, consume) for lookahead tests, which handles the end of stream safely"],"exampleFix":"// before\nchar c = stream.peek();\n// after\nif (stream.hasMore()) {\n    char c = stream.peek();\n} else {\n    // end of input: handle gracefully\n}","handlingStrategy":"type-guard","validationCode":"if (!stream.hasMore()) { /* handle end of input */ }","typeGuard":"boolean canPeek(CharacterStream s) { return s.hasMore(); }","tryCatchPattern":"try {\n    char c = stream.peek();\n} catch (RuntimeException e) {\n    if (\"No more characters in stream.\".equals(e.getMessage())) {\n        // handle EOF gracefully\n    } else throw e;\n}","preventionTips":["Gate every peek() behind hasMore()","Prefer match(needle, consume) for conditional lookahead","Terminate parse loops on !hasMore() rather than on exceptions"],"tags":["parsing","character-stream","eof"],"backgroundTag":"unexpected-end-of-input","analyzedSha":"c799cca99c7d064673dc79e6ef06a08bbc0e292d","analyzedAt":"2026-09-08T13:47:08.225Z","contentChangedAt":"2026-09-08T13:47:08.225Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}