Skip to content

[BUG] Text.wrap(console, width=0) raises ValueError in chop_cells (range step 0) #4234

Open

Description

@Sreekant13

Describe the bug

Text.wrap(console, width=0) raises ValueError: range() arg 3 must not be zero for ordinary (single-cell) text. The same call with double-width text does not crash, so the behaviour is inconsistent.

The crash is in rich/cells.py::chop_cells, whose single-cell fast path builds range(0, len(text), width):

if _is_single_cell_widths(text):
    return [text[index : index + width] for index in range(0, len(text), width)]

With width == 0 the step is 0, which range rejects. The general (wide-character) path below it has no such step and returns without raising, so chop_cells("hello", 0) crashes while chop_cells("你好", 0) does not.

It reaches the public API through Text.wrap -> _wrap.divide_line -> chop_cells (the word_length > width fold branch, taken by any non-empty word when width is 0). Width 0 is reachable when text is rendered into a zero-interior region (a table column squeezed to zero, a nested panel with no room, a console sized to 0); Rich otherwise degrades gracefully at tiny widths, so a hard crash here stands out.

Minimal repro (runs as-is):

import io
from rich.console import Console
from rich.text import Text

console = Console(file=io.StringIO())
Text("hello world").wrap(console, 0)   # ValueError: range() arg 3 must not be zero

Lower-level, showing the single-cell vs wide-char inconsistency:

from rich._wrap import divide_line
divide_line("hello", 0)   # ValueError: range() arg 3 must not be zero
divide_line("你好", 0)     # returns normally

Traceback:

  File ".../rich/text.py", line 1239, in wrap
    offsets = divide_line(str(line), width, fold=wrap_overflow == "fold")
  File ".../rich/_wrap.py", line 59, in divide_line
    folded_word = chop_cells(word, width=width)
  File ".../rich/cells.py", line 338, in chop_cells
    return [text[index : index + width] for index in range(0, len(text), width)]
ValueError: range() arg 3 must not be zero

Suggested fix

Guard chop_cells against a non-positive width (return [text] when width <= 0, since text cannot be chopped into zero-width lines). That removes the crash and also fixes the wide-character path, which for width == 0 currently returns a surprising ["", "你", "好"]. I am happy to open a PR with the guard and a regression test.

  • I've checked docs and closed issues for possible solutions.
  • I can't find my issue in the FAQ.

Platform

Click to expand

Reproduced on Rich master (commit 9d8f9a3, declared version 15.0.0), Python 3.10, Windows — but the code path is platform-independent (pure width arithmetic).

Activity

  1. UzairRehman4 commented on Oct 8, 2026

    @UzairRehman4

    I'd like to work on this, with AI assistance (Claude Code) per AI_POLICY.md — flagging that now, before any PR, since the policy requires approval here first.

    Reproduced the crash directly against current `master`:

    ```

    from rich._wrap import divide_line
    divide_line('hello', 0)
    ValueError: range() arg 3 must not be zero

    
    Implemented the suggested guard at the top of \`chop_cells\`:
    
    ```python
    if width <= 0:
        return [text]
    

    Verified against all four repro cases from the issue - none crash now:

    chop_cells('hello', 0)  -> ['hello']
    chop_cells('你好', 0)    -> ['你好']
    divide_line('hello', 0) -> []
    divide_line('你好', 0)   -> []
    Text('hello world').wrap(console, 0) -> no longer raises
    

    Also checked negative width, which wasn't in the original report but hits the same inconsistency the other way (single-cell path still raises the same `ValueError`, wide-character path silently returns `[]` instead of the whole text) — the `width <= 0` guard (rather than `width == 0`) covers that too.

    Added `tests/test_cells.py::test_chop_cells_non_positive_width`, parametrized over 0/-1/-5 against both single-cell and wide-character text. Verified it actually catches the regression: reverted the guard, confirmed all 3 cases fail with the exact described symptoms, restored it, confirmed all 72 tests in `test_cells.py` pass. Full suite otherwise unaffected (982 passed either way).

    Happy to open the PR as soon as this has the go-ahead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions