{"record":{"id":"30b3e58e21d2efbb","repo":"OtterMind/Chat2DB","slug":"missing-required-github-actions-secrets-missing","errorCode":null,"errorMessage":"Missing required GitHub Actions secrets: {missing}","messagePattern":"Missing required GitHub Actions secrets: (.+?)","errorType":"validation","errorClass":"ConfigurationError","httpStatus":null,"severity":"error","filePath":"script/github/notify_qq.py","lineNumber":575,"sourceCode":"    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}\"},\n    )\n\n\ndef _required_environment() -> tuple[str, str]:\n    names = (\"QQ_RELAY_URL\", \"QQ_RELAY_TOKEN\")\n    missing = [name for name in names if not os.environ.get(name)]\n    if missing:\n        raise ConfigurationError(\"Missing required GitHub Actions secrets: \" + \", \".join(missing))\n    return tuple(os.environ[name] for name in names)  # type: ignore[return-value]\n\n\ndef _is_true(value: str | None) -> bool:\n    return str(value or \"\").strip().lower() in {\"1\", \"true\", \"yes\", \"on\"}\n\n\ndef main() -> int:\n    event_path = Path(os.environ.get(\"GITHUB_EVENT_PATH\", \"\"))\n    if not event_path.is_file():\n        raise ConfigurationError(\"GITHUB_EVENT_PATH does not point to an event payload\")\n\n    with event_path.open(encoding=\"utf-8\") as event_file:\n        payload = json.load(event_file)\n\n    event_name = os.environ.get(\"GITHUB_EVENT_NAME\", \"\")\n    repository = os.environ.get(\"GITHUB_REPOSITORY\", \"OtterMind/Chat2DB\")\n    actor = os.environ.get(\"GITHUB_ACTOR\", \"unknown\")","sourceCodeStart":557,"sourceCodeEnd":593,"githubUrl":"https://github.com/OtterMind/Chat2DB/blob/5ee1e990e73fbcae1969dc554be254fedb3ab888/script/github/notify_qq.py#L557-L593","documentation":"Raised by _required_environment (notify_qq.py:575) as a ConfigurationError when one or both of QQ_RELAY_URL and QQ_RELAY_TOKEN are empty/unset. The message lists the missing names. This runs only on the live-send path (after dry-run/output checks), so collect and dry-run flows do not require them.","triggerScenarios":"The notifier reaches the send step but the GitHub Actions secrets QQ_RELAY_URL and/or QQ_RELAY_TOKEN were not exposed to the job, or were set to empty strings.","commonSituations":"Secrets not created in the repo; secrets created but not referenced in the job's env; secrets named differently (typo); secrets inherited conditionally from a different environment; running the workflow in a fork without the secrets.","solutions":["Create QQ_RELAY_URL and QQ_RELAY_TOKEN as repository (or environment) secrets.","Map them into the step env: QQ_RELAY_URL: ${{ secrets.QQ_RELAY_URL }}.","Check exact secret names for typos and confirm they are non-empty.","Use QQ_DRY_RUN=true or QQ_MESSAGE_OUTPUT_PATH during setup to avoid needing them until ready."],"exampleFix":"# before: env: { QQ_RELAY_URL: ${{ secrets.RELAY_URL }} }\n# after:  env: { QQ_RELAY_URL: ${{ secrets.QQ_RELAY_URL }}, QQ_RELAY_TOKEN: ${{ secrets.QQ_RELAY_TOKEN }} }","handlingStrategy":"validation","validationCode":"missing = [n for n in (\"QQ_RELAY_URL\", \"QQ_RELAY_TOKEN\") if not os.environ.get(n)]\nif missing:\n    raise SystemExit(\"missing secrets: \" + \", \".join(missing))","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Define QQ_RELAY_URL and QQ_RELAY_TOKEN as repository/environment secrets.","Map both into the step env with exact names.","Use QQ_DRY_RUN=true until the secrets are configured."],"tags":["config","security","github-actions","secrets","validation"],"backgroundTag":null,"analyzedSha":"5ee1e990e73fbcae1969dc554be254fedb3ab888","analyzedAt":"2026-08-14T07:05:03.077Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}