deepset-ai/haystack · error

CSVToDocument: quotechar must be a single character.

Error message

CSVToDocument: quotechar must be a single character.

What it means

CSVToDocument validates at construction time that both delimiter and quotechar are exactly one character, because Python's csv module requires single-character dialect parameters. A longer (or empty) string cannot be passed to csv.reader/DictReader, so the component fails fast in __init__ rather than deep inside run().

Source

Thrown at haystack/components/converters/csv.py:82

        :param conversion_mode:
            - "file" (default): one Document per CSV file whose content is the raw CSV text.
            - "row": convert each CSV row to its own Document (requires `content_column` in `run()`).
        :param delimiter:
            CSV delimiter used when parsing in row mode (passed to ``csv.DictReader``).
        :param quotechar:
            CSV quote character used when parsing in row mode (passed to ``csv.DictReader``).
        """
        self.encoding = encoding
        self.store_full_path = store_full_path
        self.conversion_mode = conversion_mode
        self.delimiter = delimiter
        self.quotechar = quotechar

        # Basic validation
        if len(self.delimiter) != 1:
            raise ValueError("CSVToDocument: delimiter must be a single character.")
        if len(self.quotechar) != 1:
            raise ValueError("CSVToDocument: quotechar must be a single character.")

    @component.output_types(documents=list[Document])
    def run(
        self,
        sources: list[str | Path | ByteStream],
        *,
        content_column: str | None = None,
        meta: dict[str, Any] | list[dict[str, Any]] | None = None,
    ) -> dict[str, Any]:
        """
        Converts CSV files to a Document (file mode) or to one Document per row (row mode).

        :param sources:
            List of file paths or ByteStream objects.
        :param content_column:
            **Required when** ``conversion_mode="row"``.
            The column name whose values become ``Document.content`` for each row.
            The column must exist in the CSV header.

View on GitHub (pinned to e318778c9b)

Solutions

  1. Pass a single-character string, e.g. delimiter=';' or delimiter='\t'.
  2. For multi-character separators, preprocess/replace the separator before feeding the CSV, or use a different converter.
  3. Ensure the value read from config/env is unquoted and is str, not bytes or list.
  4. Check for accidental leading/trailing characters (e.g. '\t ' from YAML tab expansion).

Example fix

// before
csv_conv = CSVToDocument(delimiter="||", quotechar="\"")
// after
csv_conv = CSVToDocument(delimiter="|", quotechar="\"")
# or preprocess: text = text.replace("||", "|") before conversion
Defensive patterns

Strategy: validation

Validate before calling

def validate_csv_dialect(delim, quote):
    for name, v in (("delimiter", delim), ("quotechar", quote)):
        if not isinstance(v, str) or len(v) != 1:
            raise ValueError(f"CSVToDocument {name} must be a single character, got {v!r}")
validate_csv_dialect(delimiter, quotechar)

Type guard

def is_single_char(v) -> bool:
    return isinstance(v, str) and len(v) == 1

Try / catch

try:
    conv = CSVToDocument(delimiter=delim, quotechar=quote)
except ValueError as e:
    logger.error("Bad CSV dialect: %s", e)
    conv = CSVToDocument()  # fall back to defaults

Prevention

When it happens

Trigger: Instantiating CSVToDocument(delimiter='||') or CSVToDocument(quotechar='') or any delimiter/quotechar with len != 1, e.g. multi-character separators or copying a quoted delimiter from config.

Common situations: Config supplied from YAML/env vars containing a quoted or two-char separator; users trying multi-char delimiters common in other tools (e.g. '|~|'); accidentally passing a Path or list instead of a string.

Understand the failure class

Background: "Invalid configuration value" and "Unsupported/Unknown setting value" errors: why libraries reject your config strings, numbers, and types — this error's family across 30 libraries.

Related errors


AI-assisted analysis of deepset-ai/haystack@e318778c9b (2026-08-30). Data as JSON: /api/errors/92f3002a7edb5b4d. Report an issue: GitHub.