ultralytics/ultralytics · error · ValueError

Unable to encode image source as JPEG.

Error message

Unable to encode image source as JPEG.

What it means

ValueError from LLM._to_image_url: the image was successfully loaded (or provided as an array/PIL Image), but cv2.imencode('.jpg', image) returned success=False, meaning OpenAI's required JPEG re-encode of the pixels failed. This is distinct from the read failure: pixels exist, yet encoding them to JPEG is impossible — almost always an unsupported array dtype/shape rather than a bad file.

Source

Thrown at ultralytics/models/llm.py:174

    @staticmethod
    def _image_url(source: Any) -> str:
        """Convert an image URL, path, or array to an OpenAI image URL."""
        if isinstance(source, str) and source.startswith(("http://", "https://", "data:image/")):
            return source
        if isinstance(source, (str, Path)):
            image = cv2.imread(str(source))
        else:
            image = (
                cv2.cvtColor(np.asarray(source.convert("RGB")), cv2.COLOR_RGB2BGR)
                if isinstance(source, Image.Image)
                else np.asarray(source)
            )
        if image is None:
            raise ValueError(f"Unable to read image source {source!r}.")
        success, buffer = cv2.imencode(".jpg", image)
        if not success:
            raise ValueError("Unable to encode image source as JPEG.")
        return f"data:image/jpeg;base64,{base64.b64encode(buffer).decode()}"

    def _get_client(self) -> Any:
        """Create the OpenAI client on first inference."""
        if self.client is None:
            check_requirements("openai>=2.0.0")
            from openai import OpenAI

            kwargs = {k: v for k, v in {"api_key": self._api_key, "base_url": self.base_url}.items() if v is not None}
            self.client = OpenAI(**kwargs)
        return self.client

    def _get_async_client(self) -> Any:
        """Create the asynchronous OpenAI client on first inference."""
        if self.async_client is None:
            check_requirements("openai>=2.0.0")
            from openai import AsyncOpenAI

View on GitHub (pinned to 0449ea011c)

Solutions

  1. Convert to uint8 before passing: arr = (arr * 255).clip(0,255).astype('uint8') for float data in [0,1].
  2. Ensure the array is HxWx3 BGR (or pass a PIL Image, which the code converts correctly).
  3. Save to a file or PNG/JPEG first and pass the path/URL if in doubt.
  4. Check arr.dtype and arr.shape before the call.

Example fix

# before
import numpy as np
img = np.random.rand(224, 224, 3).astype("float32")  # float -> imencode fails
result = llm(source=img)

# after
img = (np.random.rand(224, 224, 3) * 255).astype("uint8")
result = llm(source=img)
Defensive patterns

Strategy: type-guard

Validate before calling

import numpy as np

def encodable_image_array(arr) -> bool:
    return (
        isinstance(arr, np.ndarray)
        and arr.dtype == np.uint8
        and arr.ndim in {2, 3}
        and (arr.ndim == 2 or arr.shape[2] in {1, 3, 4})
        and arr.size > 0
    )

Type guard

import numpy as np

def is_uint8_image(arr) -> bool:
    """True for arrays cv2.imencode can encode: non-empty uint8 HxW or HxWx{1,3,4}."""
    return (
        isinstance(arr, np.ndarray)
        and arr.dtype == np.uint8
        and arr.size > 0
        and (arr.ndim == 2 or (arr.ndim == 3 and arr.shape[2] in {1, 3, 4}))
    )

Prevention

When it happens

Trigger: Passing a numpy array with dtype float32/float64 (imencode needs uint8), an empty 0-byte array, an array with a non-standard channel count (e.g. 4-channel BGRA is accepted, but 2 channels or exotic dtypes are not), or a PIL Image whose np.asarray conversion yields float data.

Common situations: Feeding model-preprocessing outputs (normalized float arrays in [0,1] or standardized), passing float masks/gradients meant as images, arrays created via np.zeros((h,w,3), dtype=np.float32).

Related errors


AI-assisted analysis of ultralytics/ultralytics@0449ea011c (2026-08-15). Data as JSON: /api/errors/9460fcbaa3a4f885. Report an issue: GitHub.