Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/chonkie/chunker/token.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ def __init__(
self.chunk_overlap = (
chunk_overlap if isinstance(chunk_overlap, int) else int(chunk_overlap * chunk_size)
)
# Validate the COMPUTED overlap, not just the int input: a float chunk_overlap >= 1.0
# is turned into int(chunk_overlap * chunk_size), which can be >= chunk_size and was
# skipped by the int-only guard above. That makes the step (chunk_size - chunk_overlap)
# zero or negative, so range() yields nothing and chunk() silently returns [] - the
# whole document is dropped. Enforce the docstring's contract for ints and floats alike.
if self.chunk_overlap >= self.chunk_size or self.chunk_overlap < 0:
raise ValueError("chunk_overlap must be >= 0 and less than chunk_size")
Comment on lines +59 to +65

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

There is a subtle correctness issue here with negative float overlaps. If a user passes a negative float overlap like -0.05 with chunk_size=10, the expression int(-0.05 * 10) evaluates to int(-0.5), which truncates to 0 in Python. As a result, self.chunk_overlap becomes 0, and the check self.chunk_overlap < 0 will evaluate to False, silently bypassing the validation. Checking the raw chunk_overlap input directly for negative values resolves this issue.

Suggested change
# Validate the COMPUTED overlap, not just the int input: a float chunk_overlap >= 1.0
# is turned into int(chunk_overlap * chunk_size), which can be >= chunk_size and was
# skipped by the int-only guard above. That makes the step (chunk_size - chunk_overlap)
# zero or negative, so range() yields nothing and chunk() silently returns [] - the
# whole document is dropped. Enforce the docstring's contract for ints and floats alike.
if self.chunk_overlap >= self.chunk_size or self.chunk_overlap < 0:
raise ValueError("chunk_overlap must be >= 0 and less than chunk_size")
# Validate the computed overlap and ensure the input overlap is non-negative.
# A negative float like -0.05 can truncate to 0 via int(), bypassing the < 0 check on self.chunk_overlap.
if chunk_overlap < 0 or self.chunk_overlap >= self.chunk_size:
raise ValueError("chunk_overlap must be >= 0 and less than chunk_size")


self._use_multiprocessing = False

Expand Down
25 changes: 25 additions & 0 deletions tests/test_token_overlap_validation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"""Regression test: a float chunk_overlap >= 1.0 must be rejected, not silently drop content.

TokenChunker.__init__ guarded `chunk_overlap >= chunk_size` only for int inputs. A float
like 1.5 is turned into int(1.5 * chunk_size), which can exceed chunk_size, making the step
(chunk_size - chunk_overlap) negative -> range() is empty -> chunk() returns [] and the whole
document is silently dropped from a RAG index. The docstring promises ValueError when
chunk_overlap >= chunk_size; this enforces it for floats too.

with the fix -> PASS (raises ValueError at construction; valid fractional overlap still works)
without it -> FAIL (no raise; chunk() silently returns [])
"""
import pytest

from chonkie import TokenChunker


def test_float_overlap_ge_one_is_rejected():
with pytest.raises(ValueError):
TokenChunker(tokenizer="character", chunk_size=10, chunk_overlap=1.5)
Comment on lines +17 to +19

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

It is highly recommended to add test cases verifying that negative overlaps (both integer and float) are correctly rejected during initialization.

Suggested change
def test_float_overlap_ge_one_is_rejected():
with pytest.raises(ValueError):
TokenChunker(tokenizer="character", chunk_size=10, chunk_overlap=1.5)
def test_float_overlap_ge_one_is_rejected():
with pytest.raises(ValueError):
TokenChunker(tokenizer="character", chunk_size=10, chunk_overlap=1.5)
def test_negative_overlap_is_rejected():
with pytest.raises(ValueError):
TokenChunker(tokenizer="character", chunk_size=10, chunk_overlap=-0.5)
with pytest.raises(ValueError):
TokenChunker(tokenizer="character", chunk_size=10, chunk_overlap=-1)



def test_valid_fractional_overlap_still_chunks():
chunker = TokenChunker(tokenizer="character", chunk_size=10, chunk_overlap=0.1) # -> 1 token
chunks = chunker.chunk("a" * 100)
assert len(chunks) > 0