roboflow/supervision · error · ValueError
Malformed compressed RLE string: unexpected end at position
Error message
Malformed compressed RLE string: unexpected end at position {i} What it means
The compressed-RLE decoder reads a base-48-style variable-length integer stream where each character contributes 5 bits and bit 0x20 marks continuation. If the string ends in the middle of a multi-character integer (continuation bit still set), the stream is truncated and the decoder raises with the offending position so you can locate the cut.
Source
Thrown at src/supervision/detection/utils/converters.py:439
ValueError: If the string is truncated mid-integer.
Examples:
```pycon
>>> from supervision.detection.utils.converters import _base48_decode
>>> _base48_decode("52203")
[5, 2, 2, 0, 3]
```
"""
values: list[int] = []
i = 0
while i < len(s):
x = 0
k = 0
more = True
while more:
if i >= len(s):
raise ValueError(
f"Malformed compressed RLE string: unexpected end at position {i}"
)
c = ord(s[i]) - 48
x |= (c & 0x1F) << (5 * k)
more = bool(c & 0x20)
i += 1
k += 1
if not more and (c & 0x10):
x |= ~0 << (5 * k)
values.append(x)
return values
def _base48_encode(values: list[int]) -> str:
"""Encode raw (delta-encoded) integers to a COCO base-48 string.
The inverse of :func:`_base48_decode`. Applies the same variable-length
base-48 codec used by pycocotools.View on GitHub (pinned to 7f254d9784)
Solutions
- Re-encode the mask with sv.mask_to_rle(mask, compressed=True) and compare lengths to detect truncation.
- Store compressed RLEs in binary-safe columns or verify a checksum alongside the string.
- If decoding untrusted input, wrap in try/except ValueError and drop/re-request the record.
- Pass counts as a plain list[int] (compressed=False) where transport safety is uncertain.
Example fix
# before mask = sv.rle_to_mask(rle=row["rle"][:255], resolution_wh=wh) # column truncated # after mask = sv.rle_to_mask(rle=row["rle"], resolution_wh=wh) # full string from TEXT/CLOB column
Defensive patterns
Strategy: try-catch
Validate before calling
def looks_like_compressed_rle(s: str) -> bool:
# base-48 alphabet: chars 48..95 of ASCII; and must not end mid-integer
return bool(s) and all(48 <= ord(c) <= 95 for c in s) Type guard
def is_complete_compressed_rle(s: str) -> bool:
if not s:
return False
if any(ord(c) < 48 or ord(c) > 95 for c in s):
return False
return not bool(ord(s[-1]) - 48 & 0x20) # last char must not set continuation bit Try / catch
try:
mask = sv.rle_to_mask(rle=rle_string, resolution_wh=wh)
except ValueError as e:
logger.error("Corrupt RLE record %s: %s", record_id, e)
return None # or re-fetch the record Prevention
- Use TEXT/CLOB columns or binary-safe storage for compressed RLE strings.
- Store a length or checksum beside the string and verify before decoding.
- Fall back to plain list[int] counts when transport is not ASCII-safe.
When it happens
Trigger: Passing a compressed RLE string that was truncated by a length limit, character-set filtering (e.g. non-ASCII-safe transport stripping chars), URL/JSON escaping damage, or manual copy-paste that dropped trailing characters.
Common situations: RLE strings stored in text columns with truncation; transporting via systems that mangle the alphabet (code-page conversions); concatenating or slicing encoded strings; stale data encoded by an older encoder version.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- COCO RLE counts must be one-dimensional.
- COCO RLE counts cannot be empty.
- COCO RLE counts must be non-negative.
- Invalid COCO RLE counts.
- image_shape must contain positive height and width.
AI-assisted analysis of roboflow/supervision@7f254d9784 (2026-08-15).
Data as JSON: /api/errors/39551b69a2d033c0.
Report an issue: GitHub.