sphinx-doc / sphinx-doc/sphinx
linkcheck reports uppercase-scheme URIs as broken
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 8k
- Forks
- 2.6k
- PR merge metrics
- No merged PRs in 30d
Description
Describe the bug
Sphinx's linkcheck builder uses a case-sensitive regex (([a-z]+:)?//) to detect non-HTTP URI schemes. URIs with uppercase schemes like FTP:// or HTTP:// are not recognized as external links, causing them to be checked as local file paths and reported as [broken].
Per RFC 3986 §3.1, URI schemes are case-insensitive. The code at line 471-473 of sphinx/builders/linkcheck.py intends to return unchecked for non-HTTP schemes (e.g. FTP), but fails for uppercase variants because the regex only matches lowercase.
How to Reproduce
import io
import 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())
Expected output:
index.rst:4: [unchecked] FTP://example.test/file
Actual output:
index.rst:4: [broken] FTP://example.test/file:
Environment Information
Sphinx: 9.0.4
Python: 3.11.13
Platform: Linux
Sphinx extensions
[]
Additional context
This regression was introduced by PR #7985.
The relevant code path in sphinx/builders/linkcheck.py:
uri_re = re.compile('([a-z]+:)?//') # line 64 — case-sensitive (https://github.com/sphinx-doc/sphinx/blob/master/sphinx/builders/linkcheck.py#L64)
if not uri.startswith(('http:', 'https:')):
if uri_re.match(uri): # https://github.com/sphinx-doc/sphinx/blob/master/sphinx/builders/linkcheck.py#L472
# Non-supported URI schemes (ex. ftp)
return _Status.UNCHECKED, '', 0 # ← intended for FTP://, but never reached
# falls through to local file check → reports [broken]
A fix would be making the regex case-insensitive: re.compile('([a-z]+:)?//', re.IGNORECASE) or using [a-zA-Z]+.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in sphinx/builders/linkcheck.py around the URI regex at lines 64 and 471-473, then reproduce the issue with the Sphinx application example in the report. Verify that uppercase-scheme URIs such as FTP://example.test/file are treated as external and produce [unchecked] rather than [broken], while existing HTTP and HTTPS handling remains unchanged.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- documentation
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100