{"record":{"id":"68e175f89182d2b8","repo":"tiangolo/fastapi","slug":"number-of-headers-with-permalinks-does-not-match-t","errorCode":null,"errorMessage":"Number of headers with permalinks does not match the number in the original document ({len(header_permalinks)} vs {len(original_header_permalinks)})","messagePattern":"Number of headers with permalinks 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":223,"sourceCode":"            modified_lines.append(line)\n    return modified_lines\n\n\ndef replace_header_permalinks(\n    text: list[str],\n    header_permalinks: list[HeaderPermalinkInfo],\n    original_header_permalinks: list[HeaderPermalinkInfo],\n) -> list[str]:\n    \"\"\"\n    Replace permalinks in the given text with the permalinks from the original document.\n\n    Fail if the number or level of headers does not match the original.\n    \"\"\"\n\n    modified_text: list[str] = text.copy()\n\n    if len(header_permalinks) != len(original_header_permalinks):\n        raise ValueError(\n            \"Number of headers with permalinks does not match the number in the \"\n            \"original document \"\n            f\"({len(header_permalinks)} vs {len(original_header_permalinks)})\"\n        )\n\n    for header_no in range(len(header_permalinks)):\n        header_info = header_permalinks[header_no]\n        original_header_info = original_header_permalinks[header_no]\n\n        if header_info[\"hashes\"] != original_header_info[\"hashes\"]:\n            raise ValueError(\n                \"Header levels do not match between document and original document\"\n                f\" (found {header_info['hashes']}, expected {original_header_info['hashes']})\"\n                f\" for header №{header_no + 1} in line {header_info['line_no']}\"\n            )\n        line_no = header_info[\"line_no\"] - 1\n        hashes = header_info[\"hashes\"]\n        title = header_info[\"title\"]","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/tiangolo/fastapi/blob/3e8d1526d83a90aaf7d6eb6dc682bf150f180b25/scripts/doc_parsing_utils.py#L205-L241","documentation":"Raised by `replace_header_permalinks` (scripts/doc_parsing_utils.py:223) as a ValueError when the number of headers carrying `{#permalink}` anchors in a translated document differs from the original English document. The docs translation pipeline re-applies English permalinks onto translated headers, so the header count must match exactly; a mismatch indicates the translator added/removed/merged a heading.","triggerScenarios":"Running the docs translation check on a file where a heading was deleted, added, or had its `{#...}` permalink stripped/added relative to the English source. A heading was converted to bold text or vice versa.","commonSituations":"Translators restructuring document sections. Merging/splitting headings during translation. Permalinks removed because the translator thought they were decorative. Out-of-date translation after the English doc changed heading count.","solutions":["Align the heading structure of the translation to the English source (same count of `#`/`##`/... headers).","Do not remove `{#permalink}` anchors; the tool manages them automatically.","Re-sync the translation against the latest English document."],"exampleFix":"// before\n# Translation has 2 headers, English has 3\n## Intro\n## Details\n// after\n## Intro\n## Setup\n## Details","handlingStrategy":"validation","validationCode":"from scripts.doc_parsing_utils import extract_header_permalinks\n\ndef header_counts_match(translated_lines, en_lines) -> bool:\n    return len(extract_header_permalinks(translated_lines)) == len(extract_header_permalinks(en_lines))","typeGuard":"def same_header_count(translated_lines, en_lines) -> bool:\n    from scripts.doc_parsing_utils import extract_header_permalinks\n    return len(extract_header_permalinks(translated_lines)) == len(extract_header_permalinks(en_lines))","tryCatchPattern":null,"preventionTips":["Preserve the heading structure (same number of headers) when translating.","Never strip {#permalink} anchors; the tooling manages them.","Re-check the translation whenever the English source heading count changes."],"tags":["fastapi","docs","translation","ci","validation"],"backgroundTag":null,"analyzedSha":"3e8d1526d83a90aaf7d6eb6dc682bf150f180b25","analyzedAt":"2026-08-11T02:34:52.986Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}