docling-project/docling · error · RuntimeError

An internal error has occurred during Markdown conversion.

Error message

An internal error has occurred during Markdown conversion.

What it means

Internal RuntimeError raised while converting Markdown that embeds raw HTML blocks. During parsing, HTML blocks are wrapped in _START_MARKER/_STOP_MARKER sentinels so they survive into the intermediate document; after export to HTML the backend strips them again with two regex substitutions. If the number of regex matches (re.subn count) does not equal self._html_blocks, marker insertion and removal got out of sync — an invariant violation inside the MD→HTML delegation path, not something the input file controls directly.

Source

Thrown at docling/backend/md_backend.py:815

                doc=doc,
                parent_item=None,
                visited=set(),
                creation_stack=[],
                list_ordered_flag_by_ref={},
                list_last_item_by_ref={},
            )
            self._close_table(doc=doc)  # handle any last hanging table

            # if HTML blocks were detected, export to HTML and delegate to HTML backend
            if self._html_blocks > 0:
                # export to HTML
                html_backend_cls = HTMLDocumentBackend
                html_str = doc.export_to_html()

                def _restore_original_html(txt, regex):
                    _txt, count = re.subn(regex, "", txt)
                    if count != self._html_blocks:
                        raise RuntimeError(
                            "An internal error has occurred during Markdown conversion."
                        )
                    return _txt

                # restore original HTML by removing previously added markers
                for regex in [
                    rf"<pre>\s*<code>\s*{_START_MARKER}",
                    rf"{_STOP_MARKER}\s*</code>\s*</pre>",
                ]:
                    html_str = _restore_original_html(txt=html_str, regex=regex)
                self._html_blocks = 0
                # delegate to HTML backend
                stream = BytesIO(bytes(html_str, encoding="utf-8"))
                md_options = cast(MarkdownBackendOptions, self.options)
                html_options = HTMLBackendOptions(
                    enable_local_fetch=md_options.enable_local_fetch,
                    enable_remote_fetch=md_options.enable_remote_fetch,
                    fetch_images=md_options.fetch_images,

View on GitHub (pinned to 61d76f1ff3)

Solutions

  1. Report it as a docling bug with the minimal .md file that reproduces it — the marker-count invariant is internal to md_backend.py.
  2. As a workaround, strip or fence the raw HTML blocks in the input (wrap them in ``` fences) so the HTML-delegation path is not taken.
  3. Try a different docling version: the interaction between export_to_html() formatting and the marker regexes is version-dependent.
  4. If you control the input pipeline, normalize HTML blocks (e.g. pre-convert MD HTML to plain markdown) before conversion.

Example fix

# before (raw HTML block triggers marker path)
<div class="note">hello <code>world</code></div>

# after (fenced, bypasses HTML block detection)
```html
<div class="note">hello <code>world</code></div>
```
Defensive patterns

Strategy: try-catch

Validate before calling

import re

def has_risky_html_blocks(md_text: str) -> bool:
    # raw HTML lines (not fenced) that also contain code/pre markers
    risky = re.findall(r'(?m)^(?!`{3,}).*?<(?:pre|code|div)[^>]*>.*$', md_text)
    return any('<code' in b or '<pre' in b for b in risky)

Try / catch

try:
    doc = converter.convert(md_path)
except RuntimeError as e:
    if 'internal error has occurred during Markdown conversion' in str(e):
        doc = converter.convert(sanitize_html_blocks(md_path))  # fence raw HTML first
    else:
        raise

Prevention

When it happens

Trigger: Converting a .md file whose HTML blocks, after marker wrapping and doc.export_to_html(), produce HTML where the `<pre><code>` + marker pattern is merged, duplicated, or altered (e.g. HTML blocks containing code-fence-like content, nested markers, or exporter changes that reformat `<pre><code>` whitespace). re.subn then matches a different count of markers than were inserted.

Common situations: Markdown with raw `<div>`/`<table>`/`<pre>` HTML blocks, especially ones containing literal `<code>` tags or content that the HTML exporter reindents. Also appears after upgrading docling when export_to_html() output formatting changed while md_backend marker handling did not.

Related errors


AI-assisted analysis of docling-project/docling@61d76f1ff3 (2026-08-14). Data as JSON: /api/errors/9b98f43634d4e48e. Report an issue: GitHub.