{"record":{"id":"70a8c27b4ffd72a4","repo":"tiangolo/fastapi","slug":"header-levels-do-not-match-between-document-and-or","errorCode":null,"errorMessage":"Header levels do not match between document and original document (found {header_info['hashes']}, expected {original_header_info['hashes']}) for header №{header_no + 1} in line {header_info['line_no']}","messagePattern":"Header levels do not match between document and original document \\(found (.+?), expected (.+?)\\) for header №(.+?) in line (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"scripts/doc_parsing_utils.py","lineNumber":234,"sourceCode":"\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\"]\n        permalink = original_header_info[\"permalink\"]\n        modified_text[line_no] = f\"{hashes} {title}{permalink}\"\n\n    return modified_text\n\n\n# Markdown links\n# --------------------------------------------------------------------------------------\n\n\ndef extract_markdown_links(lines: list[str]) -> list[MarkdownLinkInfo]:","sourceCodeStart":216,"sourceCodeEnd":252,"githubUrl":"https://github.com/tiangolo/fastapi/blob/3e8d1526d83a90aaf7d6eb6dc682bf150f180b25/scripts/doc_parsing_utils.py#L216-L252","documentation":"Raised by `replace_header_permalinks` (scripts/doc_parsing_utils.py:234) as a ValueError when the count of permalinks matches but the heading LEVEL (number of '#') for a given header differs between the translated and original document. For example the original has `### Setup` but the translation has `## Setup`. The error names the found/expected hashes, the 1-based header index, and the line number.","triggerScenarios":"Running the docs translation check where a translator changed `##` to `###` (or vice versa) for a heading while keeping the same total number of headers. Promoting/demoting a section's level during translation.","commonSituations":"Translators 'fixing' perceived heading hierarchy. Copy-pasting content that re-leveled headings. Inconsistent heading depth between a sub-section and its translation.","solutions":["Set the heading level to match the English original: use the same number of '#' for the corresponding header.","Open both files side by side and align hashes header-by-header at the reported line number.","Re-run the check after fixing; the error reports the next mismatch if more exist."],"exampleFix":"// before (translation line 42)\n## Setup {#setup}\n// after (match English ###)\n### Setup {#setup}","handlingStrategy":"validation","validationCode":"from scripts.doc_parsing_utils import extract_header_permalinks\n\ndef header_levels_match(translated_lines, en_lines) -> bool:\n    a = extract_header_permalinks(translated_lines)\n    b = extract_header_permalinks(en_lines)\n    return len(a) == len(b) and all(x['hashes'] == y['hashes'] for x, y in zip(a, b))","typeGuard":"def corresponding_headers_same_level(translated_lines, en_lines) -> bool:\n    from scripts.doc_parsing_utils import extract_header_permalinks\n    a = extract_header_permalinks(translated_lines)\n    b = extract_header_permalinks(en_lines)\n    return len(a) == len(b) and all(x['hashes'] == y['hashes'] for x, y in zip(a, b))","tryCatchPattern":null,"preventionTips":["Mirror the exact '#' count of each heading from the English source.","Compare files side by side when the check reports a level mismatch.","Avoid promoting/demoting headings during translation."],"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"}