Textualize/textual · error · ValueError

Invalid UTF-8 byte: {first_byte}

Error message

Invalid UTF-8 byte: {first_byte}

What it means

Raised while Textual's TextArea builds a byte-offset-to-codepoint map: the first byte of a character matches none of the UTF-8 leading-byte patterns (0xxxxxxx, 110xxxxx, 1110xxxx, 11110xxx). This means the input string's underlying bytes are not valid UTF-8, so offsets cannot be computed.

Source

Thrown at src/textual/widgets/_text_area.py:2792

    while current_byte_offset < len(data):
        byte_to_codepoint[current_byte_offset] = code_point_offset
        first_byte = data[current_byte_offset]

        # Single-byte character
        if (first_byte & 0b10000000) == 0:
            current_byte_offset += 1
        # 2-byte character
        elif (first_byte & 0b11100000) == 0b11000000:
            current_byte_offset += 2
        # 3-byte character
        elif (first_byte & 0b11110000) == 0b11100000:
            current_byte_offset += 3
        # 4-byte character
        elif (first_byte & 0b11111000) == 0b11110000:
            current_byte_offset += 4
        else:
            raise ValueError(f"Invalid UTF-8 byte: {first_byte}")

        code_point_offset += 1

    # Mapping for the end of the string
    byte_to_codepoint[current_byte_offset] = code_point_offset
    return byte_to_codepoint

View on GitHub (pinned to 06dbeef4bb)

Solutions

  1. Sanitize text before assigning to TextArea: `text.encode('utf-8', errors='ignore').decode('utf-8')` or use `errors='replace'`.
  2. Decode source data explicitly with the correct codec (e.g. `data.decode('cp1252')`) instead of assuming UTF-8.
  3. If raw bytes must be preserved, display a hex view or repr() rather than passing them as text.

Example fix

# before
text_area.text = raw_bytes.decode('utf-8', errors='surrogateescape')
# after
text_area.text = raw_bytes.decode('utf-8', errors='replace')
Defensive patterns

Strategy: validation

Validate before calling

def safe_text(raw: bytes | str) -> str:
    s = raw.decode('utf-8', errors='replace') if isinstance(raw, bytes) else raw
    s.encode('utf-8')  # raises if still invalid
    return s
text_area.text = safe_text(data)

Type guard

def is_valid_utf8_text(s: str) -> bool:
    try:
        s.encode('utf-8')
        return True
    except UnicodeEncodeError:
        return False

Try / catch

try:
    text_area.text = value
except ValueError:
    text_area.text = value.encode('utf-8', 'replace').decode('utf-8')

Prevention

When it happens

Trigger: Calling TextArea/document APIs that index locations (e.g. setting text, moving the cursor, or computing offsets) with a `str` whose bytes contain invalid UTF-8 sequences — typically text decoded with error-tolerant codecs (surrogateescape) or constructed from raw bytes containing values like 0xFF/0xFE as leading bytes.

Common situations: Loading files opened in binary and decoded with errors='replace'/'surrogateescape', embedding literal bytes in source, or receiving data from sockets/subprocess output that isn't UTF-8 (e.g. latin-1 or CP1252 logs).

Understand the failure class

Related errors


AI-assisted analysis of Textualize/textual@06dbeef4bb (2026-08-27). Data as JSON: /api/errors/e5ef5fd5f236d35a. Report an issue: GitHub.