{"record":{"id":"35d24898e8cbad53","repo":"ssssssss-team/spider-flow","slug":"start-must-be-0","errorCode":null,"errorMessage":"Start must be >= 0.","messagePattern":"Start must be >= 0\\.","errorType":"validation","errorClass":"IndexOutOfBoundsException","httpStatus":null,"severity":"error","filePath":"spider-flow-core/src/main/java/org/spiderflow/core/expression/parsing/CharacterStream.java","lineNumber":21,"sourceCode":"\nimport 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}","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/ssssssss-team/spider-flow/blob/c799cca99c7d064673dc79e6ef06a08bbc0e292d/spider-flow-core/src/main/java/org/spiderflow/core/expression/parsing/CharacterStream.java#L3-L39","documentation":"Validation guard in the CharacterStream(String source, int start, int end) constructor of the expression-language parser. It fires when the caller supplies a negative start offset (the companion check throws on start > end). The input at fault is a start index below 0, i.e. a malformed slicing window over the expression source string; index would otherwise point before the beginning of the character buffer.","triggerScenarios":"new CharacterStream(source, start, end) with start < 0, typically from indexOf() returning -1 or an unadjusted decrement past 0.","commonSituations":"Custom tokenizers using String.indexOf results directly without checking for -1.","solutions":["Check the result of indexOf() for -1 before using it as a start index","Guard with Math.max(0, startIndex) only if clamping is semantically correct","Log and handle the not-found case explicitly instead of building a stream"],"exampleFix":"// before\nint start = source.indexOf('{');\nnew CharacterStream(source, start, source.length());\n// after\nint start = source.indexOf('{');\nif (start < 0) { throw new IllegalArgumentException(\"No opening brace found\"); }\nnew CharacterStream(source, start, source.length());","handlingStrategy":"validation","validationCode":"if (start < 0) throw new IllegalArgumentException(\"start must be >= 0, got \" + start);","typeGuard":null,"tryCatchPattern":"try {\n    new CharacterStream(source, start, end);\n} catch (IndexOutOfBoundsException e) {\n    logger.error(\"Negative start index: {}\", start);\n}","preventionTips":["Always check indexOf() results for -1 before using as an index","Never decrement a start index below 0 without clamping","Validate all index arithmetic results before constructing streams"],"tags":["parsing","bounds-check","index-out-of-bounds"],"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-16T04:17:20.429Z"}