From 39d71a8ddd1fb5a3a58f8802591b08294110b853 Mon Sep 17 00:00:00 2001 From: Lengshuang <90967079+Lesereingrape@users.noreply.github.com> Date: Sat, 26 Sep 2026 14:17:43 +0800 Subject: [PATCH 1/2] fix(csv): reject an unsupported conversion_mode instead of falling into row mode CSVToDocument compared conversion_mode only against "file" in run(), so any other value selected the row branch: a typo such as "Rows" silently produced one Document per row instead of one per file, and a value differing only by trailing whitespace raised an error claiming conversion_mode='row'. Validate the Literal in __init__, the way the sibling XLSXToDocument already validates table_format and link_format. --- haystack/components/converters/csv.py | 2 ++ ...version-mode-validation-8f4c2b1d7e9a3056.yaml | 9 +++++++++ .../converters/test_csv_to_document.py | 16 ++++++++++++++++ 3 files changed, 27 insertions(+) create mode 100644 releasenotes/notes/csv-conversion-mode-validation-8f4c2b1d7e9a3056.yaml diff --git a/haystack/components/converters/csv.py b/haystack/components/converters/csv.py index 9a4a102da54..eaa70bcfdc4 100644 --- a/haystack/components/converters/csv.py +++ b/haystack/components/converters/csv.py @@ -77,6 +77,8 @@ def __init__( self.quotechar = quotechar # Basic validation + if conversion_mode not in ("file", "row"): + raise ValueError(f"CSVToDocument: unsupported conversion_mode {conversion_mode!r}. Choose 'file' or 'row'.") if len(self.delimiter) != 1: raise ValueError("CSVToDocument: delimiter must be a single character.") if len(self.quotechar) != 1: diff --git a/releasenotes/notes/csv-conversion-mode-validation-8f4c2b1d7e9a3056.yaml b/releasenotes/notes/csv-conversion-mode-validation-8f4c2b1d7e9a3056.yaml new file mode 100644 index 00000000000..c5bade60d64 --- /dev/null +++ b/releasenotes/notes/csv-conversion-mode-validation-8f4c2b1d7e9a3056.yaml @@ -0,0 +1,9 @@ +--- +fixes: + - | + ``CSVToDocument`` now rejects an unsupported ``conversion_mode`` in its constructor. + Previously the value was only compared against ``"file"`` in ``run()``, so any other + string, including a typo such as ``"Rows"`` or a value with a trailing space, silently + selected the row mode and produced one ``Document`` per row instead of one per file. + When ``content_column`` was not passed, the raised error also reported + ``conversion_mode='row'`` for a component the caller had never configured that way. diff --git a/test/components/converters/test_csv_to_document.py b/test/components/converters/test_csv_to_document.py index dfb6afcdd65..caea76fe3d5 100644 --- a/test/components/converters/test_csv_to_document.py +++ b/test/components/converters/test_csv_to_document.py @@ -190,6 +190,22 @@ def test_init_validates_delimiter_and_quotechar(self): with pytest.raises(ValueError): CSVToDocument(quotechar='""') + def test_init_rejects_an_unknown_conversion_mode(self): + # A typo must not silently switch the component into row mode: run() dispatches on the value + # and anything that is not "file" falls through to the row branch. + with pytest.raises(ValueError, match="unsupported conversion_mode 'Rows'"): + CSVToDocument(conversion_mode="Rows") + + def test_init_rejects_a_conversion_mode_that_only_differs_in_whitespace_or_case(self): + with pytest.raises(ValueError, match="unsupported conversion_mode 'file '"): + CSVToDocument(conversion_mode="file ") + with pytest.raises(ValueError, match="unsupported conversion_mode ''"): + CSVToDocument(conversion_mode="") + + def test_init_accepts_both_documented_conversion_modes(self): + assert CSVToDocument(conversion_mode="file").conversion_mode == "file" + assert CSVToDocument(conversion_mode="row").conversion_mode == "row" + def test_row_mode_large_file_warns(self, caplog, monkeypatch): # Make the threshold tiny so the warning always triggers. import haystack.components.converters.csv as csv_mod From 1ac6e39a594504ff4847fc7f97f186d13596b1cd Mon Sep 17 00:00:00 2001 From: Lesereingrape Date: Fri, 2 Oct 2026 18:23:53 +0800 Subject: [PATCH 2/2] test(csv): annotate invalid conversion_mode calls for mypy --- test/components/converters/test_csv_to_document.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/test/components/converters/test_csv_to_document.py b/test/components/converters/test_csv_to_document.py index caea76fe3d5..c79d74e9f31 100644 --- a/test/components/converters/test_csv_to_document.py +++ b/test/components/converters/test_csv_to_document.py @@ -191,16 +191,14 @@ def test_init_validates_delimiter_and_quotechar(self): CSVToDocument(quotechar='""') def test_init_rejects_an_unknown_conversion_mode(self): - # A typo must not silently switch the component into row mode: run() dispatches on the value - # and anything that is not "file" falls through to the row branch. with pytest.raises(ValueError, match="unsupported conversion_mode 'Rows'"): - CSVToDocument(conversion_mode="Rows") + CSVToDocument(conversion_mode="Rows") # type: ignore[arg-type] def test_init_rejects_a_conversion_mode_that_only_differs_in_whitespace_or_case(self): with pytest.raises(ValueError, match="unsupported conversion_mode 'file '"): - CSVToDocument(conversion_mode="file ") + CSVToDocument(conversion_mode="file ") # type: ignore[arg-type] with pytest.raises(ValueError, match="unsupported conversion_mode ''"): - CSVToDocument(conversion_mode="") + CSVToDocument(conversion_mode="") # type: ignore[arg-type] def test_init_accepts_both_documented_conversion_modes(self): assert CSVToDocument(conversion_mode="file").conversion_mode == "file"