{"record":{"id":"c8503145c26dd7a2","repo":"ssssssss-team/spider-flow","slug":"start-outside-of-string","errorCode":null,"errorMessage":"Start outside of string.","messagePattern":"Start outside of string\\.","errorType":"validation","errorClass":"IndexOutOfBoundsException","httpStatus":null,"severity":"error","filePath":"spider-flow-core/src/main/java/org/spiderflow/core/expression/parsing/CharacterStream.java","lineNumber":22,"sourceCode":"import javax.xml.transform.Source;\n\n/** Wraps a the content of a {@link Source} and handles traversing the contained characters. Manages a current {@link Span} via\n * the {@link #startSpan()} and {@link #endSpan()} methods. */\npublic class CharacterStream {\n\tprivate final String source;\n\tprivate int index = 0;\n\tprivate final int end;\n\n\tprivate int spanStart = 0;\n\n\tpublic CharacterStream (String source) {\n\t\tthis(source, 0, source.length());\n\t}\n\n\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","sourceCodeStart":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/ssssssss-team/spider-flow/blob/c799cca99c7d064673dc79e6ef06a08bbc0e292d/spider-flow-core/src/main/java/org/spiderflow/core/expression/parsing/CharacterStream.java#L4-L40","documentation":"CharacterStream's constructor rejects a start offset beyond the last character of the source string, throwing IndexOutOfBoundsException(\"Start outside of string.\"). The start index must point at or before an existing character.","triggerScenarios":"new CharacterStream(source, start, end) where start > source.length()-1 (e.g. start == source.length() on a non-empty string, or any start on an empty string).","commonSituations":"Slicing from an index at end-of-input after consuming all tokens; passing a full-string length as the start by swapping arguments.","solutions":["Validate start < source.length() before constructing","Handle the end-of-input case without constructing a new stream (hasMore-style check)","Check argument order — start and end may be swapped","Note: on an empty source string any start will fail; special-case it"],"exampleFix":"// before\nnew CharacterStream(source, source.length(), source.length());\n// after\nif (start < source.length()) {\n    new CharacterStream(source, start, end);\n} else {\n    // at end of input, nothing to parse\n}","handlingStrategy":"validation","validationCode":"if (source == null || source.length() == 0 || start >= source.length()) return null; // nothing to stream","typeGuard":null,"tryCatchPattern":"try {\n    new CharacterStream(source, start, end);\n} catch (IndexOutOfBoundsException e) {\n    logger.error(\"Start {} outside source of length {}\", start, source == null ? 0 : source.length());\n}","preventionTips":["Check for end-of-input before slicing a new stream","Remember empty strings fail any start; special-case them","Validate argument order (start before end)"],"tags":["parsing","bounds-check","character-stream"],"backgroundTag":"index-out-of-bounds","analyzedSha":"c799cca99c7d064673dc79e6ef06a08bbc0e292d","analyzedAt":"2026-09-08T13:47:08.225Z","contentChangedAt":"2026-09-08T13:47:08.225Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}