ssssssss-team/spider-flow · error · IndexOutOfBoundsException

Start must be >= 0.

Error message

Start must be >= 0.

What it means

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.

Solutions

  1. Check the result of indexOf() for -1 before using it as a start index
  2. Guard with Math.max(0, startIndex) only if clamping is semantically correct
  3. Log and handle the not-found case explicitly instead of building a stream

Example fix

// before
int start = source.indexOf('{');
new CharacterStream(source, start, source.length());
// after
int start = source.indexOf('{');
if (start < 0) { throw new IllegalArgumentException("No opening brace found"); }
new CharacterStream(source, start, source.length());
Defensive patterns

Strategy: validation

Validate before calling

if (start < 0) throw new IllegalArgumentException("start must be >= 0, got " + start);

Try / catch

try {
    new CharacterStream(source, start, end);
} catch (IndexOutOfBoundsException e) {
    logger.error("Negative start index: {}", start);
}

Prevention

When it happens

Trigger: new CharacterStream(source, start, end) with start < 0, typically from indexOf() returning -1 or an unadjusted decrement past 0.

Common situations: Custom tokenizers using String.indexOf results directly without checking for -1.

Related errors


AI-assisted analysis of ssssssss-team/spider-flow@c799cca99c (2026-09-08). Data as JSON: /api/errors/35d24898e8cbad53. Report an issue: GitHub.

Appendix: source

Thrown at spider-flow-core/src/main/java/org/spiderflow/core/expression/parsing/CharacterStream.java:21

import javax.xml.transform.Source;

/** Wraps a the content of a {@link Source} and handles traversing the contained characters. Manages a current {@link Span} via
 * the {@link #startSpan()} and {@link #endSpan()} methods. */
public class CharacterStream {
	private final String source;
	private int index = 0;
	private final int end;

	private int spanStart = 0;

	public CharacterStream (String source) {
		this(source, 0, source.length());
	}

	public CharacterStream (String source, int start, int end) {
		if (start > end) throw new IllegalArgumentException("Start must be <= end.");
		if (start < 0) throw new IndexOutOfBoundsException("Start must be >= 0.");
		if (start > Math.max(0, source.length() - 1)) throw new IndexOutOfBoundsException("Start outside of string.");
		if (end > source.length()) throw new IndexOutOfBoundsException("End outside of string.");

		this.source = source;
		this.index = start;
		this.end = end;
	}

	/** Returns whether there are more characters in the stream **/
	public boolean hasMore () {
		return index < end;
	}

	/** Returns the next character without advancing the stream **/
	public char peek () {
		if (!hasMore()) throw new RuntimeException("No more characters in stream.");
		return source.charAt(index++);
	}

View on GitHub (pinned to c799cca99c)