ssssssss-team/spider-flow · error · IndexOutOfBoundsException

Start outside of string.

Error message

Start outside of string.

What it means

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.

Solutions

  1. Validate start < source.length() before constructing
  2. Handle the end-of-input case without constructing a new stream (hasMore-style check)
  3. Check argument order — start and end may be swapped
  4. Note: on an empty source string any start will fail; special-case it

Example fix

// before
new CharacterStream(source, source.length(), source.length());
// after
if (start < source.length()) {
    new CharacterStream(source, start, end);
} else {
    // at end of input, nothing to parse
}
Defensive patterns

Strategy: validation

Validate before calling

if (source == null || source.length() == 0 || start >= source.length()) return null; // nothing to stream

Try / catch

try {
    new CharacterStream(source, start, end);
} catch (IndexOutOfBoundsException e) {
    logger.error("Start {} outside source of length {}", start, source == null ? 0 : source.length());
}

Prevention

When it happens

Trigger: 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).

Common situations: Slicing from an index at end-of-input after consuming all tokens; passing a full-string length as the start by swapping arguments.

Related errors


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

Appendix: source

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

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)