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
- Convert to uint8 before passing: arr = (arr * 255).clip(0,255).astype('uint8') for float data in [0,1].
- Ensure the array is HxWx3 BGR (or pass a PIL Image, which the code converts correctly).
- Save to a file or PNG/JPEG first and pass the path/URL if in doubt.
- 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
- Convert float arrays (model outputs, normalized images) with .clip(0,255).astype('uint8') before passing.
- Pass PIL Images directly — the wrapper handles their conversion correctly.
- Log arr.dtype/arr.shape in wrappers that forward arbitrary arrays to the LLM.
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
- No images found in source, predict requires at least one ima
- Expected PIL/np.ndarray image type, but got {type(im)}
- Expected a single (H, W, C) image, but got array of shape {i
- type {type(im).__name__} is not a supported Ultralytics pred
- Unsupported API format {!r}. Use 'responses' or 'chat.comple
AI-assisted analysis of ultralytics/ultralytics@0449ea011c (2026-08-15).
Data as JSON: /api/errors/9460fcbaa3a4f885.
Report an issue: GitHub.