Skip to content

fix(miner): reject chunk_overlap above half the chunk size to stop chunk_text hang (#2056) - #2058

Merged
igorls merged 1 commit into
MemPalace:developfrom
mvalentsev:fix/2056-chunk-text-windowing-loop
Jul 22, 2026
Merged

fix(miner): reject chunk_overlap above half the chunk size to stop chunk_text hang (#2056)#2058
igorls merged 1 commit into
MemPalace:developfrom
mvalentsev:fix/2056-chunk-text-windowing-loop

Conversation

@mvalentsev

Copy link
Copy Markdown
Contributor

What does this PR do?

chunk_text (mempalace/miner.py) can loop forever at 100% CPU when chunk_overlap is set above half the chunk size and the content has short lines. This tightens the existing overlap guard, in both chunk_text and MempalaceConfig._validated_chunk_config, from chunk_overlap >= chunk_size to chunk_overlap > chunk_size // 2, the exact point past which the windowing loop can stop advancing.

Fixes #2056.

Root cause. The windowing loop advances with start = end - chunk_overlap. A paragraph or line boundary pull only moves end back when the boundary sits past start + chunk_size // 2, so a pulled chunk always spans more than chunk_size // 2 characters. The step therefore advances only while chunk_overlap <= chunk_size // 2; a larger overlap makes start stall or move backward, and on short-line content the loop oscillates over the same positions and never returns. The existing guards were added to stop exactly this loop (the config docstring and the chunk_text error message both say such an overlap "would loop forever"), but they only rejected chunk_overlap >= chunk_size, so any chunk_size // 2 < chunk_overlap < chunk_size slipped through.

The boundary is exactly chunk_size // 2.

  • chunk_overlap <= chunk_size // 2 always terminates: after a pull end - start > chunk_size // 2 >= chunk_overlap, so start advances by at least one. Confirmed for every chunk_size in 4..400.
  • chunk_overlap == chunk_size // 2 + 1 can hang: content whose lines are about half the chunk size makes each pulled chunk span exactly chunk_size // 2 + 1, so start does not move. Confirmed for every chunk_size in 4..400, and on the real chunk_text (chunk_size=20, chunk_overlap=11 hangs; 20/10 returns).

So 50% overlap (chunk_overlap == chunk_size // 2) stays valid; only strictly more than half is rejected, which narrows no useful configuration.

Fix.

  • chunk_text (a public function): raise ValueError for chunk_overlap > chunk_size // 2, with the numeric bound in the message.
  • MempalaceConfig._validated_chunk_config: a config.json overlap above half is repaired to min(DEFAULT_CHUNK_OVERLAP, chunk_size // 2), which is never above half. This matches the file's existing rule that a bad config.json repairs rather than raises.

The windowing loop itself is untouched, so every already-valid config produces byte-identical chunks.

Behavior change. Direct chunk_text callers now get a fast ValueError for chunk_size // 2 < chunk_overlap < chunk_size, where before they got a hang (or, on sparse content, a result). This also covers the module default overlap (100) when a caller overrides chunk_size below 200 without passing an explicit overlap; the old guard already raised the same way for chunk_size <= 100. CLI and MCP users are unaffected: both mining paths read the validated chunk_overlap property, which clamps an out-of-range overlap down to chunk_size // 2 rather than raising. The default 800 / 100 config, and any overlap up to half the chunk size, are unchanged.

This follows #2054 / #2055 (the O(N*K) line-locator cost), which flagged this loop as a separate bug. The partial guard came from #1024.

How to test

Reproduce on develop, then confirm it is fixed here. Pure chunk_text, no palace needed:

from mempalace.miner import chunk_text

# Hangs at 100% CPU on develop; raises a clear ValueError with this change.
chunk_text("abc\n" * 50, "/x.md", chunk_size=50, chunk_overlap=49)

Through the mempalace binary: set "chunk_size": 120, "chunk_overlap": 61 in a palace config.json and mempalace mine a file whose lines are short. On develop the mine sits at 100% CPU and never completes; with this change it finishes (the overlap is repaired to 60).

Automated:

uv run pytest tests/test_config.py -v   # guard, repair, and exact-boundary tests
uv run pytest tests/ -v                 # full suite
ruff check .

The full suite is green apart from two pre-existing concurrency-lock timing tests (test_writer_blocks_during_mine, test_mine_convos_refuses_concurrent_run_against_same_palace) that flake on develop as well and never touch chunking.

Checklist

  • Tests pass (python -m pytest tests/ -v)
  • No hardcoded paths
  • Linter passes (ruff check .)

…unk_text hang (MemPalace#2056)

chunk_text's windowing loop (start = end - chunk_overlap) stops advancing
when chunk_overlap exceeds chunk_size // 2 on short-line content, looping
forever at 100% CPU. A paragraph or line boundary pull only moves end past
start + chunk_size // 2, so a pulled chunk spans more than half the chunk
size; the step then advances only while chunk_overlap <= chunk_size // 2.

Tighten the existing guard from chunk_overlap >= chunk_size to
chunk_overlap > chunk_size // 2 in both chunk_text (raise, with the numeric
bound in the message) and MempalaceConfig._validated_chunk_config (repair to
min(DEFAULT_CHUNK_OVERLAP, chunk_size // 2)). The windowing loop is
unchanged, so every already-valid config produces byte-identical chunks; 50%
overlap (chunk_overlap == chunk_size // 2) stays valid.

Fixes MemPalace#2056.
@mvalentsev
mvalentsev force-pushed the fix/2056-chunk-text-windowing-loop branch from a5a5c99 to bfd1e48 Compare July 21, 2026 19:34
@mvalentsev
mvalentsev marked this pull request as ready for review July 21, 2026 19:34
@igorls
igorls merged commit 39d151b into MemPalace:develop Jul 22, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

chunk_text windowing loops forever when chunk_overlap >= chunk_size // 2 on short-line content

2 participants