{"record":{"id":"43e712a887185ade","repo":"OtterMind/Chat2DB","slug":"qq-relay-url-must-be-an-https-url-without-credenti","errorCode":null,"errorMessage":"QQ_RELAY_URL must be an HTTPS URL without credentials or a fragment","messagePattern":"QQ_RELAY_URL must be an HTTPS URL without credentials or a fragment","errorType":"validation","errorClass":"ConfigurationError","httpStatus":null,"severity":"error","filePath":"script/github/notify_qq.py","lineNumber":549,"sourceCode":"                raise relay_error from error\n        except URLError as error:\n            if attempt == 2:\n                raise RuntimeError(f\"QQ relay network request failed: {error.reason}\") from error\n        time.sleep(2**attempt)\n\n    raise AssertionError(\"unreachable\")\n\n\ndef _validated_relay_url(value: str) -> str:\n    parsed = urlparse(value)\n    if (\n        parsed.scheme != \"https\"\n        or not parsed.hostname\n        or parsed.username\n        or parsed.password\n        or parsed.fragment\n    ):\n        raise ConfigurationError(\"QQ_RELAY_URL must be an HTTPS URL without credentials or a fragment\")\n    return value\n\n\ndef send_relay_message(\n    relay_url: str,\n    relay_token: str,\n    repository: str,\n    delivery_id: str,\n    content: str,\n) -> dict[str, Any]:\n    return _post_json(\n        _validated_relay_url(relay_url),\n        {\n            \"repository\": repository,\n            \"delivery_id\": delivery_id,\n            \"message\": content,\n        },\n        {\"Authorization\": f\"Bearer {relay_token}\"},","sourceCodeStart":531,"sourceCodeEnd":567,"githubUrl":"https://github.com/OtterMind/Chat2DB/blob/5ee1e990e73fbcae1969dc554be254fedb3ab888/script/github/notify_qq.py#L531-L567","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before: QQ_RELAY_URL=http://relay.local:8080/v1/qq/github\n# after:  QQ_RELAY_URL=https://relay.example.com/v1/qq/github","handlingStrategy":"validation","validationCode":"from urllib.parse import urlparse\np = urlparse(relay_url)\nassert p.scheme == \"https\" and p.hostname and not p.username and not p.password and not p.fragment, \\\n    \"QQ_RELAY_URL must be https, no credentials, no fragment\"","typeGuard":"from urllib.parse import urlparse\ndef is_valid_relay_url(value: str) -> bool:\n    p = urlparse(value)\n    return (p.scheme == \"https\" and bool(p.hostname)\n            and not p.username and not p.password and not p.fragment)","tryCatchPattern":null,"preventionTips":["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."],"tags":["config","security","network","github-actions","validation"],"backgroundTag":null,"analyzedSha":"5ee1e990e73fbcae1969dc554be254fedb3ab888","analyzedAt":"2026-08-14T07:05:03.077Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}