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
- Set QQ_RELAY_URL to an https:// URL with a hostname and no credentials or fragment.
- For local testing, terminate TLS (e.g. via a tunnel) rather than using http.
- Move credentials into the Authorization header (QQ_RELAY_TOKEN), never into the URL.
- 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
- Store QQ_RELAY_URL as https only; terminate TLS even for local testing.
- Put credentials in QQ_RELAY_TOKEN, never in the URL.
- Avoid copying URLs with #fragments from a browser.
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
- Prepared QQ notification contains a OneBot CQ code
- Missing required GitHub Actions secrets: {missing}
- Cannot collect unsupported event: {event_name}
- Prepared QQ notification is missing or too large
- Prepared QQ notification repository does not match
AI-assisted analysis of OtterMind/Chat2DB@5ee1e990e7 (2026-08-14).
Data as JSON: /api/errors/43e712a887185ade.
Report an issue: GitHub.