OtterMind/Chat2DB · error · ConfigurationError

QQ_RELAY_URL must be an HTTPS URL without credentials or a f

Error message

QQ_RELAY_URL must be an HTTPS URL without credentials or a fragment

What it means

Raised by _validated_relay_url (notify_qq.py:549) as a ConfigurationError when QQ_RELAY_URL is not a valid HTTPS URL. It rejects any scheme other than https, URLs without a hostname, URLs containing embedded credentials (user:pass@), and URLs with a fragment. This runs before any network call, inside send_relay_message.

Source

Thrown at script/github/notify_qq.py:549

                raise relay_error from error
        except URLError as error:
            if attempt == 2:
                raise RuntimeError(f"QQ relay network request failed: {error.reason}") from error
        time.sleep(2**attempt)

    raise AssertionError("unreachable")


def _validated_relay_url(value: str) -> str:
    parsed = urlparse(value)
    if (
        parsed.scheme != "https"
        or not parsed.hostname
        or parsed.username
        or parsed.password
        or parsed.fragment
    ):
        raise ConfigurationError("QQ_RELAY_URL must be an HTTPS URL without credentials or a fragment")
    return value


def send_relay_message(
    relay_url: str,
    relay_token: str,
    repository: str,
    delivery_id: str,
    content: str,
) -> dict[str, Any]:
    return _post_json(
        _validated_relay_url(relay_url),
        {
            "repository": repository,
            "delivery_id": delivery_id,
            "message": content,
        },
        {"Authorization": f"Bearer {relay_token}"},

View on GitHub (pinned to 5ee1e990e7)

Solutions

  1. Set QQ_RELAY_URL to an https:// URL with a hostname and no credentials or fragment.
  2. For local testing, terminate TLS (e.g. via a tunnel) rather than using http.
  3. Move credentials into the Authorization header (QQ_RELAY_TOKEN), never into the URL.
  4. Re-paste the secret value without a trailing fragment or whitespace.

Example fix

# before: QQ_RELAY_URL=http://relay.local:8080/v1/qq/github
# after:  QQ_RELAY_URL=https://relay.example.com/v1/qq/github
Defensive patterns

Strategy: validation

Validate before calling

from urllib.parse import urlparse
p = urlparse(relay_url)
assert p.scheme == "https" and p.hostname and not p.username and not p.password and not p.fragment, \
    "QQ_RELAY_URL must be https, no credentials, no fragment"

Type guard

from urllib.parse import urlparse
def is_valid_relay_url(value: str) -> bool:
    p = urlparse(value)
    return (p.scheme == "https" and bool(p.hostname)
            and not p.username and not p.password and not p.fragment)

Prevention

When it happens

Trigger: QQ_RELAY_URL uses http:// (blocked), is empty/garbage (no hostname), includes user:pass@ credentials, or carries a #fragment. Any of these aborts the send.

Common situations: Configuring QQ_RELAY_URL with a plaintext http relay for local testing; pasting a URL with credentials; trailing #anchor copied from a browser; a missing or malformed secret value in GitHub Actions.

Related errors


AI-assisted analysis of OtterMind/Chat2DB@5ee1e990e7 (2026-08-14). Data as JSON: /api/errors/43e712a887185ade. Report an issue: GitHub.