chroma-core/chroma · error · ValueError

The PIL python package is not installed. Please install it w

Error message

The PIL python package is not installed. Please install it with `pip install pillow`

What it means

ImageLoader is chromadb's DataLoader that turns image URIs into numpy arrays, and it does so through Pillow: its constructor calls importlib.import_module("PIL.Image") and, if that raises ImportError, re-raises it as this ValueError. Pillow is an optional dependency, so a plain `pip install chromadb` does not pull it in. The error surfaces at construction time, before any image is actually loaded.

Source

Thrown at chromadb/utils/data_loaders.py:15

import importlib
import multiprocessing
from typing import Optional, Sequence, List, Tuple
import numpy as np
from chromadb.api.types import URI, DataLoader, Image, URIs
from concurrent.futures import ThreadPoolExecutor


class ImageLoader(DataLoader[List[Optional[Image]]]):
    def __init__(self, max_workers: int = multiprocessing.cpu_count()) -> None:
        try:
            self._PILImage = importlib.import_module("PIL.Image")
            self._max_workers = max_workers
        except ImportError:
            raise ValueError(
                "The PIL python package is not installed. Please install it with `pip install pillow`"
            )

    def _load_image(self, uri: Optional[URI]) -> Optional[Image]:
        return np.array(self._PILImage.open(uri)) if uri is not None else None

    def __call__(self, uris: Sequence[Optional[URI]]) -> List[Optional[Image]]:
        with ThreadPoolExecutor(max_workers=self._max_workers) as executor:
            return list(executor.map(self._load_image, uris))


class ChromaLangchainPassthroughDataLoader(DataLoader[List[Optional[Image]]]):
    # This is a simple pass through data loader that just returns the input data with "images"
    # flag which lets the langchain embedding function know that the data is image uris
    def __call__(self, uris: URIs) -> Tuple[str, URIs]:  # type: ignore
        return ("images", uris)

View on GitHub (pinned to aecdd12c8a)

Solutions

  1. Install pillow in the active environment: pip install pillow (or add `pillow` to requirements.txt next to chromadb).
  2. Verify the install lands in the interpreter you run: python -c "import PIL; print(PIL.__version__)".
  3. If you do not actually need image decoding, drop ImageLoader entirely and stop requesting include=["images"].

Example fix

# before: raises ValueError "The PIL python package is not installed..."
from chromadb.utils.data_loaders import ImageLoader
loader = ImageLoader()

# after: install pillow first (pip install pillow), then
from chromadb.utils.data_loaders import ImageLoader
loader = ImageLoader()
col = client.get_collection("imgs", embedding_function=ef, data_loader=loader)
Defensive patterns

Strategy: validation

Validate before calling

import importlib.util

if importlib.util.find_spec("PIL") is None:
    raise RuntimeError("pillow is required for ImageLoader; run: pip install pillow")

from chromadb.utils.data_loaders import ImageLoader
loader = ImageLoader()

Try / catch

from chromadb.utils.data_loaders import ImageLoader
try:
    loader = ImageLoader()
except ValueError as e:
    if "PIL" in str(e):
        raise RuntimeError("Install pillow to load images: pip install pillow") from e
    raise

Prevention

When it happens

Trigger: Instantiating chromadb.utils.data_loaders.ImageLoader() in an environment where pillow is not installed, typically to pass it as data_loader= to client.get_collection(), client.list_collections(), or to use include=["images"] / add() with URIs on a multimodal collection.

Common situations: Fresh virtualenv or CI runner with only chromadb installed; slim Docker images (python:*-slim) that skip build deps; tutorials on multimodal collections that assume pillow is present but never list it in requirements.

Related errors


AI-assisted analysis of chroma-core/chroma@aecdd12c8a (2026-08-16). Data as JSON: /api/errors/d6e4fc83a4bbb58b. Report an issue: GitHub.