{"record":{"id":"636bc9f2cb53bb07","repo":"tiangolo/fastapi","slug":"number-of-html-links-does-not-match-the-number-in","errorCode":null,"errorMessage":"Number of HTML links does not match the number in the original document ({len(links)} vs {len(original_links)})","messagePattern":"Number of HTML links does not match the number in the original document \\((.+?) vs (.+?)\\)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"scripts/doc_parsing_utils.py","lineNumber":447,"sourceCode":"    )\n    return f\"<a {attrs_str}>{link_text}</a>\"\n\n\ndef replace_html_links(\n    text: list[str],\n    links: list[HtmlLinkInfo],\n    original_links: list[HtmlLinkInfo],\n    lang_code: str,\n) -> list[str]:\n    \"\"\"\n    Replace HTML links in the given text with the links from the original document.\n\n    Adjust URLs for the given language code.\n    Fail if the number of links does not match the original.\n    \"\"\"\n\n    if len(links) != len(original_links):\n        raise ValueError(\n            \"Number of HTML links does not match the number in the \"\n            \"original document \"\n            f\"({len(links)} vs {len(original_links)})\"\n        )\n\n    modified_text = text.copy()\n    for link_index, link in enumerate(links):\n        original_link_info = original_links[link_index]\n\n        # Replace in the document text\n        replacement_link = _construct_html_link(\n            link_text=link[\"text\"],\n            attributes=original_link_info[\"attributes\"],\n            lang_code=lang_code,\n        )\n        line_no = link[\"line_no\"] - 1\n        modified_text[line_no] = modified_text[line_no].replace(\n            link[\"full_tag\"], replacement_link, 1","sourceCodeStart":429,"sourceCodeEnd":465,"githubUrl":"https://github.com/tiangolo/fastapi/blob/3e8d1526d83a90aaf7d6eb6dc682bf150f180b25/scripts/doc_parsing_utils.py#L429-L465","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Match the count and order of `<a>` tags to the English source.","Keep `<a>` tags well-formed with at least one attribute after `<a` so the regex matches.","Do not mix HTML `<a>` links with markdown links when translating; preserve the original format."],"exampleFix":"// before (translation has 1 <a>, English has 2)\n<a href=\"/x\">x</a>\n// after\n<a href=\"/x\">x</a> <a href=\"/y\">y</a>","handlingStrategy":"validation","validationCode":"from scripts.doc_parsing_utils import extract_html_links\n\ndef html_link_counts_match(translated_lines, en_lines) -> bool:\n    return len(extract_html_links(translated_lines)) == len(extract_html_links(en_lines))","typeGuard":"def same_html_link_count(translated_lines, en_lines) -> bool:\n    from scripts.doc_parsing_utils import extract_html_links\n    return len(extract_html_links(translated_lines)) == len(extract_html_links(en_lines))","tryCatchPattern":null,"preventionTips":["Preserve <a> tags verbatim (count and order) when translating.","Keep HTML anchors well-formed so the regex matches them.","Do not convert HTML links to markdown or vice versa."],"tags":["fastapi","docs","translation","ci","validation"],"backgroundTag":null,"analyzedSha":"3e8d1526d83a90aaf7d6eb6dc682bf150f180b25","analyzedAt":"2026-08-11T02:34:52.986Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}