From 51b2fa38de42dba88cccb762dc8da60a0043a56f Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 17 Sep 2026 00:34:42 +0100 Subject: [PATCH] fix: refuse a Redis URL whose port cannot be used urlsplit accepts an authority such as localhost:notaport or localhost:65536 and urlunsplit rebuilds it verbatim; only parts.port raises, and nothing read it. The unusable value reached .env, where the worker picked it up and failed to start its broker, while `up` reported success because it waits only on the API health endpoint. --- docsgpt/deploy/commands.py | 7 +++++++ tests/deploy/test_native.py | 9 +++++++++ 2 files changed, 16 insertions(+) diff --git a/docsgpt/deploy/commands.py b/docsgpt/deploy/commands.py index 19babb5a..db114639 100644 --- a/docsgpt/deploy/commands.py +++ b/docsgpt/deploy/commands.py @@ -215,6 +215,13 @@ def _redis_urls(base: str) -> dict[str, str]: except ValueError as exc: # urlsplit raises on things like redis://[::1 ; that is a typo, not a crash. raise DeployError(f"the Redis URL {base!r} could not be read: {exc}") from exc + try: + _ = parts.port # a non-numeric or out-of-range port raises here, not when the URL is split + except ValueError as exc: + raise DeployError( + f"the Redis URL {base!r} has an unusable port: {exc}. Pass a URL like " + "redis://host:6379 or redis://host:6379/5." + ) from exc if parts.scheme not in ("redis", "rediss"): raise DeployError( f"the Redis URL {base!r} should start with redis:// or rediss://, with any options as " diff --git a/tests/deploy/test_native.py b/tests/deploy/test_native.py index 33a50568..a9335068 100644 --- a/tests/deploy/test_native.py +++ b/tests/deploy/test_native.py @@ -231,6 +231,15 @@ class TestNativeUp: with pytest.raises(DeployError, match="too long to read"): _run(argv, _native_context()) + @pytest.mark.parametrize("url", ["redis://localhost:notaport/0", "redis://localhost:65536/0"]) + def test_a_redis_url_with_an_unusable_port_is_refused(self, tmp_path, url): + """urlsplit accepts it and rebuilds it verbatim; only .port notices, and the worker dies later.""" + argv = ["up", "--native", "--dir", str(tmp_path), "--yes", + "--postgres-uri", "postgresql://localhost/d", "--redis-url", url] + with pytest.raises(DeployError, match="unusable port"): + _run(argv, _native_context()) + assert not (tmp_path / ".env").exists(), "it refuses before writing settings the worker would read" + @pytest.mark.parametrize("url", ["localhost:6379", "redis+socket:///var/run/redis.sock", "redis://localhost:6379/queue"]) def test_a_redis_url_that_cannot_be_numbered_is_refused(self, tmp_path, url):