tiangolo/fastapi · error · ValueError

Number of HTML links does not match the number in the origin

Error message

Number of HTML links does not match the number in the original document ({len(links)} vs {len(original_links)})

What it means

Raised by `replace_html_links` (scripts/doc_parsing_utils.py:447) as a ValueError when the number of HTML `<a ...>...</a>` links in a translated document differs from the original English document. As with markdown links, the pipeline rewrites href values positionally, so HTML link counts must match. A mismatch means an HTML link was added, removed, or its tag structure changed so the regex no longer matches.

Source

Thrown at scripts/doc_parsing_utils.py:447

    )
    return f"<a {attrs_str}>{link_text}</a>"


def replace_html_links(
    text: list[str],
    links: list[HtmlLinkInfo],
    original_links: list[HtmlLinkInfo],
    lang_code: str,
) -> list[str]:
    """
    Replace HTML links in the given text with the links from the original document.

    Adjust URLs for the given language code.
    Fail if the number of links does not match the original.
    """

    if len(links) != len(original_links):
        raise ValueError(
            "Number of HTML links does not match the number in the "
            "original document "
            f"({len(links)} vs {len(original_links)})"
        )

    modified_text = text.copy()
    for link_index, link in enumerate(links):
        original_link_info = original_links[link_index]

        # Replace in the document text
        replacement_link = _construct_html_link(
            link_text=link["text"],
            attributes=original_link_info["attributes"],
            lang_code=lang_code,
        )
        line_no = link["line_no"] - 1
        modified_text[line_no] = modified_text[line_no].replace(
            link["full_tag"], replacement_link, 1

View on GitHub (pinned to 3e8d1526d8)

Solutions

  1. Match the count and order of `<a>` tags to the English source.
  2. Keep `<a>` tags well-formed with at least one attribute after `<a` so the regex matches.
  3. Do not mix HTML `<a>` links with markdown links when translating; preserve the original format.

Example fix

// before (translation has 1 <a>, English has 2)
<a href="/x">x</a>
// after
<a href="/x">x</a> <a href="/y">y</a>
Defensive patterns

Strategy: validation

Validate before calling

from scripts.doc_parsing_utils import extract_html_links

def html_link_counts_match(translated_lines, en_lines) -> bool:
    return len(extract_html_links(translated_lines)) == len(extract_html_links(en_lines))

Type guard

def same_html_link_count(translated_lines, en_lines) -> bool:
    from scripts.doc_parsing_utils import extract_html_links
    return len(extract_html_links(translated_lines)) == len(extract_html_links(en_lines))

Prevention

When it happens

Trigger: Running the docs translation check where a translated file contains a different number of `<a href=...>` tags than the English original. Reformatting an HTML link as markdown or vice versa. A broken/self-closing tag that the `HTML_LINK_RE` regex skips.

Common situations: Translators removing an `<a>` tag or replacing it with markdown syntax. Malformed HTML where a missing attribute breaks the `<a\s+[^>]*>.*?</a>` match. Adding new anchor tags not present in the source.

Related errors


AI-assisted analysis of tiangolo/fastapi@3e8d1526d8 (2026-08-11). Data as JSON: /api/errors/636bc9f2cb53bb07. Report an issue: GitHub.