diff --git a/docs/about/release-notes.md b/docs/about/release-notes.md index 4cc040ab..06da0671 100644 --- a/docs/about/release-notes.md +++ b/docs/about/release-notes.md @@ -23,6 +23,12 @@ The current members of the MkDocs-NG team. * [@shenxianpeng](https://github.com/shenxianpeng) +## Version 1.7.2 (Unreleased) + +### Fixed + +* Fix malformed URLs (e.g., unterminated IPv6 literals) crashing the entire build. #45 + ## Version 1.7.1 (2026-04-25) ### Fixed diff --git a/mkdocs/structure/pages.py b/mkdocs/structure/pages.py index ca37d797..8fd47dd7 100644 --- a/mkdocs/structure/pages.py +++ b/mkdocs/structure/pages.py @@ -441,7 +441,20 @@ def _possible_target_uris( tried.add(guess) def path_to_url(self, url: str) -> str: - scheme, netloc, path, query, anchor = urlsplit(url) + try: + scheme, netloc, path, query, anchor = urlsplit(url) + except ValueError as e: + # The URL parser raised on a malformed link target — for example + # `[click here](http://[::1)`. Per mkdocs#3939, a single bad + # link should not crash the entire build. Log a warning that + # mentions the file and the offending URL, then leave the link + # text untouched in the rendered HTML so the rest of the page + # still builds. + log.warning( + f"Doc file '{self.file.src_uri}' contains an invalid URL '{url}': {e}. " + "The link is being left unchanged." + ) + return url absolute_link = None warning_level, warning = 0, "" diff --git a/mkdocs/tests/build_tests.py b/mkdocs/tests/build_tests.py index 504a662b..dae3b9f3 100644 --- a/mkdocs/tests/build_tests.py +++ b/mkdocs/tests/build_tests.py @@ -551,6 +551,28 @@ def on_page_context(*args, **kwargs): # Test build.build + @tempdir(files={"index.md": "[click here](http://[::1)\n"}) + @tempdir() + def test_build_warns_and_continues_on_invalid_link_url(self, site_dir, docs_dir): + # Regression for mkdocs/mkdocs#3939: a single markdown link with a + # malformed URL (e.g. an unterminated IPv6 literal) used to crash + # the entire build with `ValueError: Invalid IPv6 URL`. Per the + # maintainer's direction on the issue, mkdocs should now log a + # warning that names the file and the offending URL and keep + # building. + cfg = load_config(docs_dir=docs_dir, site_dir=site_dir, nav=["index.md"]) + with self.assertLogs("mkdocs", level="WARNING") as cm: + build.build(cfg) + warning_messages = [ + r.getMessage() for r in cm.records if r.levelname == "WARNING" + ] + self.assertTrue( + any("'index.md'" in m and "http://[::1" in m for m in warning_messages), + f"Expected a warning naming index.md and the bad URL, got: {warning_messages}", + ) + # The page was still produced. + self.assertPathIsFile(site_dir, "index.html") + @tempdir( files={ "index.md": "page content",