deepset-ai/haystack · error · ValueError

split_overlap must be greater than or equal to 0.

Error message

split_overlap must be greater than or equal to 0.

What it means

RecursiveSplitter requires split_overlap >= 0; a negative overlap is meaningless for sliding-window chunking. _check_params runs at construction time so invalid configuration fails immediately.

Source

Thrown at haystack/components/preprocessors/recursive_splitter.py:117

    def warm_up(self) -> None:
        """
        Warm up the sentence tokenizer and tiktoken tokenizer if needed.
        """
        if self._is_warmed_up:
            return
        if "sentence" in self.separators:
            self.nltk_tokenizer = self._get_custom_sentence_tokenizer(self.sentence_splitter_params)
        if self.split_units == "token":
            tiktoken_imports.check()
            self.tiktoken_tokenizer = tiktoken.get_encoding("o200k_base")
        self._is_warmed_up = True

    def _check_params(self) -> None:
        if self.split_length < 1:
            raise ValueError("Split length must be at least 1 character.")
        if self.split_overlap < 0:
            raise ValueError("split_overlap must be greater than or equal to 0.")
        if self.split_overlap >= self.split_length:
            raise ValueError("Overlap cannot be greater than or equal to the chunk size.")
        if not all(isinstance(separator, str) for separator in self.separators):
            raise ValueError("All separators must be strings.")

    @staticmethod
    def _get_custom_sentence_tokenizer(sentence_splitter_params: dict[str, Any]) -> Any:
        from haystack.components.preprocessors.sentence_tokenizer import SentenceSplitter

        return SentenceSplitter(**sentence_splitter_params)

    def _split_chunk(self, current_chunk: str) -> tuple[str, str]:
        """
        Splits a chunk based on the split_length and split_units attribute.

        :param current_chunk: The current chunk to be split.
        :returns:
            A tuple containing the current chunk and the remaining chunk.

View on GitHub (pinned to e318778c9b)

Solutions

  1. Set split_overlap to 0 (no overlap) or a positive value less than split_length.
  2. Clamp: max(0, computed_overlap).
  3. Remember overlap must also satisfy split_overlap < split_length (next check).

Example fix

// before
splitter = RecursiveSplitter(split_length=200, split_overlap=-10)
// after
splitter = RecursiveSplitter(split_length=200, split_overlap=20)
Defensive patterns

Strategy: validation

Validate before calling

if split_overlap < 0:
    raise ValueError(f'split_overlap must be >= 0, got {split_overlap}')

Type guard

def is_valid_overlap(v) -> bool:
    return isinstance(v, int) and v >= 0

Try / catch

try:
    splitter = RecursiveSplitter(split_length=200, split_overlap=ov)
except ValueError as e:
    logger.error('Invalid split_overlap: %s', e)
    splitter = RecursiveSplitter(split_length=200, split_overlap=0)

Prevention

When it happens

Trigger: RecursiveSplitter(split_overlap=-N) with any negative integer; computed overlap values that went negative; sign confusion between 'gap' and 'overlap' semantics.

Common situations: Configs migrated from components where the parameter represented a gap (to be subtracted); arithmetic like split_length - step returning negative.

Understand the failure class

Background: "must be positive", "Invalid value": how libraries reject invalid parameter values (ValueError, ArgumentError, INVALID_PARAMETER_VALUE) — this error's family across 28 libraries.

Related errors


AI-assisted analysis of deepset-ai/haystack@e318778c9b (2026-08-30). Data as JSON: /api/errors/b2fb0e04182f64bd. Report an issue: GitHub.