tiangolo/fastapi · error · ValueError

Number of headers with permalinks does not match the number

Error message

Number of headers with permalinks does not match the number in the original document ({len(header_permalinks)} vs {len(original_header_permalinks)})

What it means

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.

Source

Thrown at scripts/doc_parsing_utils.py:223

            modified_lines.append(line)
    return modified_lines


def replace_header_permalinks(
    text: list[str],
    header_permalinks: list[HeaderPermalinkInfo],
    original_header_permalinks: list[HeaderPermalinkInfo],
) -> list[str]:
    """
    Replace permalinks in the given text with the permalinks from the original document.

    Fail if the number or level of headers does not match the original.
    """

    modified_text: list[str] = text.copy()

    if len(header_permalinks) != len(original_header_permalinks):
        raise ValueError(
            "Number of headers with permalinks does not match the number in the "
            "original document "
            f"({len(header_permalinks)} vs {len(original_header_permalinks)})"
        )

    for header_no in range(len(header_permalinks)):
        header_info = header_permalinks[header_no]
        original_header_info = original_header_permalinks[header_no]

        if header_info["hashes"] != original_header_info["hashes"]:
            raise ValueError(
                "Header levels do not match between document and original document"
                f" (found {header_info['hashes']}, expected {original_header_info['hashes']})"
                f" for header №{header_no + 1} in line {header_info['line_no']}"
            )
        line_no = header_info["line_no"] - 1
        hashes = header_info["hashes"]
        title = header_info["title"]

View on GitHub (pinned to 3e8d1526d8)

Solutions

  1. Align the heading structure of the translation to the English source (same count of `#`/`##`/... headers).
  2. Do not remove `{#permalink}` anchors; the tool manages them automatically.
  3. Re-sync the translation against the latest English document.

Example fix

// before
# Translation has 2 headers, English has 3
## Intro
## Details
// after
## Intro
## Setup
## Details
Defensive patterns

Strategy: validation

Validate before calling

from scripts.doc_parsing_utils import extract_header_permalinks

def header_counts_match(translated_lines, en_lines) -> bool:
    return len(extract_header_permalinks(translated_lines)) == len(extract_header_permalinks(en_lines))

Type guard

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

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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