docling-project/docling · error · ValueError

prompt must be str or list[str], got {type(prompt)}

Error message

prompt must be str or list[str], got {type(prompt)}

What it means

Strictest prompt typing of the VLM engines: the MLX raw-image path accepts only str (broadcast) or list (one per image). Passing a tuple, generator, dict, or None raises ValueError naming the received type — unlike sibling models, there is no implicit iteration over other sequences.

Source

Thrown at docling/models/vlm_pipeline_models/mlx_model.py:204

        # Convert image batch to list for length validation
        image_list = list(image_batch)

        if len(image_list) == 0:
            return

        # Handle prompt parameter
        if isinstance(prompt, str):
            # Single prompt for all images
            user_prompts = [prompt] * len(image_list)
        elif isinstance(prompt, list):
            # List of prompts (one per image)
            if len(prompt) != len(image_list):
                raise ValueError(
                    f"Number of prompts ({len(prompt)}) must match number of images ({len(image_list)})"
                )
            user_prompts = prompt
        else:
            raise ValueError(f"prompt must be str or list[str], got {type(prompt)}")

        # MLX models are not thread-safe - use global lock to serialize access
        with _MLX_GLOBAL_LOCK:
            _log.debug("MLX model: Acquired global lock for thread safety")
            for image, user_prompt in zip(image_list, user_prompts):
                # Convert numpy array to PIL Image if needed
                if isinstance(image, np.ndarray):
                    if image.ndim == 3 and image.shape[2] in [3, 4]:
                        # RGB or RGBA array
                        from PIL import Image as PILImage

                        image = PILImage.fromarray(image.astype(np.uint8))
                    elif image.ndim == 2:
                        # Grayscale array
                        from PIL import Image as PILImage

                        image = PILImage.fromarray(image.astype(np.uint8), mode="L")
                    else:

View on GitHub (pinned to 61d76f1ff3)

Solutions

  1. Materialize sequences: prompt = list(prompt_tuple_or_gen) before the call
  2. Unwrap Optionals: prompt = prompt or DEFAULT_PROMPT
  3. Use a plain string for the shared-instruction case

Example fix

# before
model.process_images(images, (f"p{i}" for i in ids))  # generator -> ValueError
# after
model.process_images(images, [f"p{i}" for i in ids])
Defensive patterns

Strategy: type-guard

Validate before calling

if not isinstance(prompt, (str, list)):
    prompt = list(prompt) if hasattr(prompt, '__iter__') else str(prompt)
assert isinstance(prompt, (str, list))

Type guard

from typing import Union

def narrow_mlx_prompt(p) -> Union[str, list[str]]:
    if isinstance(p, (tuple, set)) or hasattr(p, '__next__'):
        return list(p)
    if not isinstance(p, (str, list)):
        raise TypeError(f'prompt must be str or list[str], got {type(p)!r}')
    return p

Try / catch

try:
    model.process_images(images, prompt)
except ValueError as e:
    if 'prompt must be str or list' in str(e):
        model.process_images(images, list(prompt) if not isinstance(prompt, str) else prompt)
    else:
        raise

Prevention

When it happens

Trigger: Passing a tuple or generator expression as prompt, or None from an unset Optional[str], to the MLX model's raw-image processing API.

Common situations: Passing a generator expression expecting lazy consumption; feeding a tuple from a loader API; forgetting to unwrap an Optional[str] that defaults to None.

Related errors


AI-assisted analysis of docling-project/docling@61d76f1ff3 (2026-08-14). Data as JSON: /api/errors/2ccd4fba5cabd48a. Report an issue: GitHub.