{"record":{"id":"08b8824f8a870ef1","repo":"docling-project/docling","slug":"unsupported-vlm-response-format-self-pipeline-opt","errorCode":null,"errorMessage":"Unsupported VLM response format {self.pipeline_options.vlm_options.response_format}. Only DOCTAGS format is supported.","messagePattern":"Unsupported VLM response format (.+?)\\. Only DOCTAGS format is supported\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"docling/experimental/pipeline/threaded_layout_vlm_pipeline.py","lineNumber":408,"sourceCode":"        # Clean up images if not needed for remaining pages\n        if not self.pipeline_options.generate_page_images:\n            for p in conv_res.pages:\n                p._image_cache = {}\n\n    def _assemble_document(self, conv_res: ConversionResult) -> ConversionResult:\n        \"\"\"Assemble final document from VLM predictions.\"\"\"\n        from docling_core.types.doc import DocItem, ImageRef, PictureItem\n\n        from docling.datamodel.pipeline_options_vlm_model import ResponseFormat\n\n        with TimeRecorder(conv_res, \"doc_assemble\", scope=ProfilingScope.DOCUMENT):\n            # Response format validation is done in ThreadedLayoutVlmPipelineOptions\n            # This check is kept as a safety net, but should never trigger if validation works\n            if (\n                self.pipeline_options.vlm_options.response_format\n                != ResponseFormat.DOCTAGS\n            ):\n                raise RuntimeError(\n                    f\"Unsupported VLM response format {self.pipeline_options.vlm_options.response_format}. Only DOCTAGS format is supported.\"\n                )\n            conv_res.document = self._turn_dt_into_doc(conv_res)\n\n            # Generate images of the requested element types\n            if self.pipeline_options.generate_picture_images:\n                # Create mapping from page_no to Page object since pages may be non-continuous\n                page_map = {p.page_no: p for p in conv_res.pages}\n                scale = self.pipeline_options.images_scale\n                for element, _level in conv_res.document.iterate_items():\n                    if not isinstance(element, DocItem) or len(element.prov) == 0:\n                        continue\n                    if (\n                        isinstance(element, PictureItem)\n                        and self.pipeline_options.generate_picture_images\n                    ):\n                        page_no = element.prov[0].page_no\n                        page = page_map.get(page_no)","sourceCodeStart":390,"sourceCodeEnd":426,"githubUrl":"https://github.com/docling-project/docling/blob/61d76f1ff3f8428065465889f7b4577da7df704c/docling/experimental/pipeline/threaded_layout_vlm_pipeline.py#L390-L426","documentation":"RuntimeError safety net in ThreadedLayoutVlmPipeline's document assembly: the VLM response format must be DOCTAGS for the assembly stage to turn predictions into a document. Normally the Pydantic validator on the options class rejects other formats earlier, so hitting this means options were mutated after validation or bypassed validation.","triggerScenarios":"Constructing valid options with DOCTAGS, then reassigning pipeline_options.vlm_options.response_format to MARKDOWN/JSON at runtime before conversion; or injecting options objects built with model_construct (validation skipped).","commonSituations":"Tweaking a shared options object between runs (e.g. toggling response_format for a markdown-side experiment) without rebuilding the pipeline; deserializing options from config without revalidation.","solutions":["Set response_format=ResponseFormat.DOCTAGS and rebuild the pipeline options object so the validator runs again.","Treat pipeline options as immutable after pipeline construction; create a new pipeline for a new configuration.","When deserializing options, round-trip through the Pydantic model (parse_obj) rather than dict patching."],"exampleFix":"# before\npipeline.pipeline_options.vlm_options.response_format = ResponseFormat.MARKDOWN  # mutation\nconv_res = pipeline(doc)\n\n# after\nfrom docling.datamodel.pipeline_options_vlm_model import ResponseFormat\nopts = ThreadedLayoutVlmPipelineOptions(\n    vlm_options=VlmOptions(response_format=ResponseFormat.DOCTAGS)\n)\npipeline = ThreadedLayoutVlmPipeline(opts)","handlingStrategy":"validation","validationCode":"from docling.datamodel.pipeline_options_vlm_model import ResponseFormat\nassert pipeline.pipeline_options.vlm_options.response_format == ResponseFormat.DOCTAGS, 'rebuild options with DOCTAGS'","typeGuard":null,"tryCatchPattern":"try:\n    conv_res = pipeline(in_doc)\nexcept RuntimeError as e:\n    if 'Only DOCTAGS format is supported' in str(e):\n        raise SystemExit('options mutated after validation; rebuild pipeline with DOCTAGS options')","preventionTips":["Treat pipeline options as immutable once the pipeline is constructed.","Rebuild options objects (and pipelines) on any configuration change instead of mutating fields."],"tags":["vlm","doctags","immutability","safety-net"],"backgroundTag":null,"analyzedSha":"61d76f1ff3f8428065465889f7b4577da7df704c","analyzedAt":"2026-08-14T23:53:18.727Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}