#467·PageIndex

Oversized first page produces an empty chunk that reaches the model

Author: jerrl10Created Sep 2, 2026Updated Sep 2, 2026

When the first page of a document exceeds the per-part token average, page_list_to_group_text() emits an empty string as its first chunk. Both callers pass that chunk to a model call as if it were a slice of the document.

Reproduction

python
from pageindex.page_index_classic import page_list_to_group_text

print(page_list_to_group_text(["PAGE-A", "PAGE-B", "PAGE-C"],
                              [60000, 500, 500], max_tokens=20000))
# ['', 'PAGE-A', 'PAGE-APAGE-B', 'PAGE-BPAGE-C']
#  ^^ empty

Root cause

page_index_classic.py:530-537 — at i == 0 the accumulator is still empty, so the boundary test reduces to token_lengths[0] > average_tokens_per_part and flushes before anything has been added:

python
if current_token_count + page_tokens > average_tokens_per_part:
    subsets.append(''.join(current_subset))     # current_subset is [] when i == 0

Only the first page can trigger it — later splits re-seed the accumulator with the overlap page. [60000, 500, 500] reproduces; [100, 60000, 100] does not.

Consequence

  • process_no_toc() (:686)generate_toc_init() seeds the whole table of contents from an empty prompt. The empty chunk has no <physical_index_N> markers, so _validate_chunk_physical_indices() nullifies everything it returns, and that seed is carried into every generate_toc_continue() call (:698). No page content is lost (page 1 arrives via the overlap in chunk 1) and the bogus entries are filtered at :1221 — the cost is a wasted call and a contaminated seed.
  • process_toc_no_page_numbers() (:738) — this loop does not skip chunk 0. add_page_number_to_toc() is asked to assign page numbers from an empty document, and a mismatched entry count raises ValueError at :742, aborting the run.

Scope

average_tokens_per_part is bounded below by max_tokens / 2, so the first page must exceed ~15k tokens (~60 KB) — a wide table, a dense appendix, a badly paginated export. Not the common case, and call site 1 fails silently, which is likely why it has gone unnoticed. Also reachable per-node: process_large_node_recursively() (:1174) re-runs process_no_toc over one oversized node, so "first page" there is the node's.