Summary
On a rate-limit pause, the refinement pass and the plain-text pipeline carefully build a "remaining work" list and then raise — but that list is a local that the exception discards, and neither path has partial-state persistence. So auto-pause/resume restarts from zero, re-paying for every chunk already refined/translated.
Where
src/core/translator.py:826-835 (refine_chunks fills refined_parts then raises; only caller src/core/refine/txt_refiner.py:106 just propagates)
src/core/common/plain_text_pipeline.py:193-197 (_fill_remaining_with_source() then raise rate_limit_error; filled translated_parts lost)
Details
refine_chunks appends the remaining unrefined chunks to refined_parts and raises on RateLimitError, but refined_parts is a local that's thrown away — refine-only has no checkpointing, so resume restarts refinement from chunk 0. The pre-raise list-filling is dead code that misleadingly suggests partial results are kept.
The plain-text pipeline has the same shape: it fills translated_parts with source then raises, losing the filled list (DOCX/EPUB plain mode returns b'' on interruption and resumes from scratch).
Impact
Wasted API spend and time: every already-completed refine/plain-mode chunk is redone after an auto-pause.
Suggested fix
Either add partial-state persistence to the refine-only and plain-text paths (checkpoint completed chunks like the main pipelines do), or remove the misleading dead list-filling and document that these paths restart on pause. Persisting is the better fix given auto-pause is a normal occurrence under rate limits.
Found during the June 2026 repo audit. Severity: medium. Confidence: certain.
Summary
On a rate-limit pause, the refinement pass and the plain-text pipeline carefully build a "remaining work" list and then
raise— but that list is a local that the exception discards, and neither path has partial-state persistence. So auto-pause/resume restarts from zero, re-paying for every chunk already refined/translated.Where
src/core/translator.py:826-835(refine_chunksfillsrefined_partsthen raises; only callersrc/core/refine/txt_refiner.py:106just propagates)src/core/common/plain_text_pipeline.py:193-197(_fill_remaining_with_source()thenraise rate_limit_error; filledtranslated_partslost)Details
refine_chunksappends the remaining unrefined chunks torefined_partsand raises onRateLimitError, butrefined_partsis a local that's thrown away — refine-only has no checkpointing, so resume restarts refinement from chunk 0. The pre-raise list-filling is dead code that misleadingly suggests partial results are kept.The plain-text pipeline has the same shape: it fills
translated_partswith source then raises, losing the filled list (DOCX/EPUB plain mode returnsb''on interruption and resumes from scratch).Impact
Wasted API spend and time: every already-completed refine/plain-mode chunk is redone after an auto-pause.
Suggested fix
Either add partial-state persistence to the refine-only and plain-text paths (checkpoint completed chunks like the main pipelines do), or remove the misleading dead list-filling and document that these paths restart on pause. Persisting is the better fix given auto-pause is a normal occurrence under rate limits.
Found during the June 2026 repo audit. Severity: medium. Confidence: certain.