{"record":{"id":"30e31c2d10c45bb9","repo":"deepset-ai/haystack","slug":"header-split-levels-must-be-a-non-empty-list","errorCode":null,"errorMessage":"header_split_levels must be a non-empty list.","messagePattern":"header_split_levels must be a non-empty list\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"haystack/components/preprocessors/markdown_header_splitter.py","lineNumber":62,"sourceCode":"            Defaults to True.\n        :param header_split_levels: List of header levels (1–6) to split on. For example, `[1, 2]` splits only\n            on `#` and `##` headers, merging content under deeper headers into the preceding chunk. Defaults to\n            all levels `[1, 2, 3, 4, 5, 6]`.\n        :param secondary_split: Optional secondary split condition after header splitting.\n            Options are None, \"word\", \"passage\", \"period\", \"line\". Defaults to None.\n        :param split_length: The maximum number of units in each split when using secondary splitting. Defaults to 200.\n        :param split_overlap: The number of overlapping units for each split when using secondary splitting.\n            Defaults to 0.\n        :param split_threshold: The minimum number of units per split when using secondary splitting. Defaults to 0.\n        :param skip_empty_documents: Choose whether to skip documents with empty content. Default is True.\n            Set to False when downstream components in the Pipeline (like LLMDocumentContentExtractor) can extract text\n            from non-textual documents.\n        \"\"\"\n        if header_split_levels is None:\n            header_split_levels = [1, 2, 3, 4, 5, 6]\n\n        if not isinstance(header_split_levels, list) or len(header_split_levels) == 0:\n            raise ValueError(\"header_split_levels must be a non-empty list.\")\n        invalid = [lvl for lvl in header_split_levels if not isinstance(lvl, int) or lvl < 1 or lvl > 6]\n        if invalid:\n            raise ValueError(\n                f\"header_split_levels contains invalid values: {invalid}. All levels must be integers between 1 and 6.\"\n            )\n        if len(header_split_levels) != len(set(header_split_levels)):\n            raise ValueError(\"header_split_levels must not contain duplicate values.\")\n\n        self.page_break_character = page_break_character\n        self.secondary_split = secondary_split\n        self.split_length = split_length\n        self.split_overlap = split_overlap\n        self.split_threshold = split_threshold\n        self.skip_empty_documents = skip_empty_documents\n        self.keep_headers = keep_headers\n        self.header_split_levels = header_split_levels\n        self._header_split_levels_set = set(header_split_levels)\n        self._header_pattern = re.compile(r\"(?m)^(#{1,6}) (.+)$\")  # ATX-style .md-headers","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/deepset-ai/haystack/blob/e318778c9bf60a1963e3b5f451359655dd696c30/haystack/components/preprocessors/markdown_header_splitter.py#L44-L80","documentation":"MarkdownHeaderSplitter's __init__ validates header_split_levels. If it is not a list or is an empty list, a ValueError is thrown because the splitter needs at least one header level (1-6) on which to split markdown documents.","triggerScenarios":"Calling MarkdownHeaderSplitter(header_split_levels=[]) or header_split_levels set to a non-list value such as a string, tuple, or int (e.g. header_split_levels=2 instead of [2]).","commonSituations":"Passing a single int instead of a list, building the levels list dynamically and ending up empty, or deserializing component config from JSON/YAML where the list was omitted or emptied.","solutions":["Pass a non-empty list of integers, e.g. header_split_levels=[1, 2, 3].","Omit the parameter entirely to use the default [1, 2, 3, 4, 5, 6].","Wrap a scalar level in a list: header_split_levels=[2] not header_split_levels=2.","Ensure dynamic/config-driven values are validated for non-emptiness before constructing the component."],"exampleFix":"// before\nMarkdownHeaderSplitter(header_split_levels=2)\nMarkdownHeaderSplitter(header_split_levels=[])\n// after\nMarkdownHeaderSplitter(header_split_levels=[1, 2])\nMarkdownHeaderSplitter()  # defaults to [1,2,3,4,5,6]","handlingStrategy":"validation","validationCode":"def valid_split_levels(v):\n    return isinstance(v, list) and len(v) > 0 and all(isinstance(l, int) and 1 <= l <= 6 for l in v)\nif not valid_split_levels(header_split_levels):\n    header_split_levels = [1, 2, 3, 4, 5, 6]","typeGuard":"def is_header_split_levels(v) -> bool:\n    return isinstance(v, list) and len(v) > 0 and all(isinstance(l, int) and not isinstance(l, bool) and 1 <= l <= 6 for l in v)","tryCatchPattern":"try:\n    splitter = MarkdownHeaderSplitter(header_split_levels=levels)\nexcept ValueError as e:\n    logging.warning(\"Bad header_split_levels (%s), using default\", e)\n    splitter = MarkdownHeaderSplitter()","preventionTips":["Never pass a bare int; always wrap a single level in a list.","Omit the parameter when you want all heading levels (the default).","Validate config-driven lists for type and non-emptiness before construction.","Remember bool is a subclass of int; sanitize user input with explicit int checks."],"tags":["python","validation","constructor-argument","haystack"],"backgroundTag":"invalid-constructor-argument","analyzedSha":"e318778c9bf60a1963e3b5f451359655dd696c30","analyzedAt":"2026-08-30T11:45:20.711Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}