Skip to content

Fix linkcheck uppercase scheme handling - #14640

Draft
aryansk wants to merge 1 commit into
sphinx-doc:masterfrom
aryansk:fix-linkcheck-uppercase-scheme-14541
Draft

Fix linkcheck uppercase scheme handling#14640
aryansk wants to merge 1 commit into
sphinx-doc:masterfrom
aryansk:fix-linkcheck-uppercase-scheme-14541

Conversation

@aryansk

@aryansk aryansk commented Aug 25, 2026

Copy link
Copy Markdown

Problem

Sphinx's linkcheck builder uses a case-sensitive regex (([a-z]+:)?//) and case-sensitive startswith checks for http:/https: to detect external links. Per RFC 3986 §3.1, URI schemes are case-insensitive. URIs with uppercase schemes like FTP://example.test/file or HTTP://example.com are not recognized:

  • FTP:// fails uri_re.match (only lowercase [a-z]), so it falls through to the local-file existence check and is reported as [broken] instead of [unchecked].
  • HTTP:///HTTPS:// fails uri.startswith(('http:', 'https:')), also falling through to local-file logic.

Fixes #14541

Reproduction from the issue:

import io, shutil
from pathlib import Path
from sphinx.application import Sphinx

root = Path("/tmp/sphinx_linkcheck_test")
shutil.rmtree(root, ignore_errors=True)
(root / "_build").mkdir(parents=True)
(root / "conf.py").write_text("")
(root / "index.rst").write_text("Test\n====\n\n`download <FTP://example.test/file>`_\n")

app = Sphinx(str(root), str(root), str(root / "_build"), str(root / "_doctree"),
             "linkcheck", status=io.StringIO(), warning=io.StringIO())
app.build(force_all=True)
print((root / "_build" / "output.txt").read_text())
# Before fix: index.rst:4: [broken] FTP://example.test/file:
# After fix: status unchecked (via output.json)

Change

The smallest complete fix in sphinx/builders/linkcheck.py:

  • uri_re = re.compile('([a-z]+:)?//', re.IGNORECASE) — recognize uppercase schemes
  • uri.lower().startswith(('http:', 'https:')) — handle HTTP:///HTTPS:// correctly
  • uri.lower().startswith(('#', 'mailto:', 'tel:')) — consistent for MAILTO:/TEL: (also case-insensitive per RFC)

Why this approach

  • re.IGNORECASE preserves the existing pattern while matching uppercase per RFC 3986.
  • lower().startswith is the standard case-insensitive prefix check and matches how urlsplit/urlparse normalize schemes.
  • No behavioral change for lowercase URIs; only fixes the uppercase regression introduced by PR Close #5208: linkcheck: Support checks for local links #7985.

Testing

Manual reproduction with patched code:

command: uv run --project /tmp/sphinx-l1-14541/repo python /tmp/test_sphinx2.py
result: FTP://example.test/file -> status unchecked (PASS), HTTP:// -> network check (redirected), MAILTO: -> unchecked

Existing linkcheck suite:

command: uv run --project /tmp/sphinx-l1-14541/repo pytest /tmp/sphinx-l1-14541/repo/tests/test_builders/test_build_linkcheck.py -v
result: 48 passed in 4.39s

Documentation and release impact

  • No documentation impact (bug fix only)
  • Changelog/release note needed — will add if maintainer requests

Review notes

AI disclosure

AI assistance was used (Muse Spark) for code analysis and fix drafting. All changes were manually reviewed, tested locally, and verified against the reproduction case. The contributor understands and can explain the submitted code.

The linkcheck builder used case-sensitive checks for URI schemes.
URIs with uppercase schemes like FTP:// or HTTP:// were not
recognized as external links and were incorrectly reported as
broken due to falling through to the local file existence check.

- Make uri_re case-insensitive (re.IGNORECASE) to correctly
  identify non-HTTP schemes regardless of case
- Make the http/https and mailto/tel prefix checks case-insensitive
  via uri.lower().startswith()

Fixes sphinx-doc#14541

AI assistance disclosure: Muse Spark used for code analysis and
fix generation; all changes reviewed, tested locally (48 linkcheck
tests passing) and verified against the reproduction case.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

linkcheck reports uppercase-scheme URIs as broken

1 participant