{"record":{"id":"060749f8282c5ccd","repo":"OtterMind/Chat2DB","slug":"a-delivery-id-is-required-for-relay-deduplication","errorCode":null,"errorMessage":"A delivery ID is required for relay deduplication","messagePattern":"A delivery ID is required for relay deduplication","errorType":"validation","errorClass":"ConfigurationError","httpStatus":null,"severity":"error","filePath":"script/github/notify_qq.py","lineNumber":621,"sourceCode":"    else:\n        message = build_notification(\n            event_name, payload, repository, actor, run_url, include_url=include_url\n        )\n        action = _clean_text(payload.get(\"action\") or \"manual\", 60)\n\n        output_path = os.environ.get(\"QQ_MESSAGE_OUTPUT_PATH\")\n        if output_path:\n            _write_prepared_message(Path(output_path), event_name, repository, message)\n            print(f\"QQ notification collected: event={event_name}, action={action}\")\n            return 0\n\n    if _is_true(os.environ.get(\"QQ_DRY_RUN\")):\n        print(f\"QQ notification dry run passed: event={event_name}, action={action}\")\n        return 0\n\n    delivery_id = _clean_text(os.environ.get(\"QQ_DELIVERY_ID\") or run_id, 160)\n    if not delivery_id:\n        raise ConfigurationError(\"A delivery ID is required for relay deduplication\")\n    relay_url, relay_token = _required_environment()\n    response = send_relay_message(\n        relay_url, relay_token, repository, delivery_id, message\n    )\n    message_id = _clean_text(response.get(\"message_id\"), 160)\n    duplicate = bool(response.get(\"duplicate\"))\n    url_removed = bool(response.get(\"url_removed\"))\n    suffixes = []\n    if duplicate:\n        suffixes.append(\"duplicate suppressed\")\n    if url_removed:\n        suffixes.append(\"URL removed by fallback\")\n    suffix = \"; \" + \"; \".join(suffixes) if suffixes else \"\"\n    print(f\"QQ notification sent: message_id={message_id or 'unknown'}{suffix}\")\n    return 0\n\n\nif __name__ == \"__main__\":","sourceCodeStart":603,"sourceCodeEnd":639,"githubUrl":"https://github.com/OtterMind/Chat2DB/blob/5ee1e990e73fbcae1969dc554be254fedb3ab888/script/github/notify_qq.py#L603-L639","documentation":"Raised by main (notify_qq.py:621) as a ConfigurationError when the computed delivery_id is empty. The id is QQ_DELIVERY_ID if set, otherwise GITHUB_RUN_ID, cleaned to 160 chars. The relay uses it for idempotency/deduplication, so an empty id aborts the send. Dry-run and output modes return earlier and do not need it.","triggerScenarios":"Both QQ_DELIVERY_ID and GITHUB_RUN_ID are empty/unset on the live-send path. QQ_DELIVERY_ID is empty after cleaning and GITHUB_RUN_ID is not provided by the runner.","commonSituations":"Running the notifier outside GitHub Actions without QQ_DELIVERY_ID; a custom runner that does not export GITHUB_RUN_ID; an environment where GITHUB_RUN_ID is explicitly blanked.","solutions":["Run inside GitHub Actions where GITHUB_RUN_ID is always set.","For manual/dispatch sends, set QQ_DELIVERY_ID to a unique non-empty value (alphanumerics, dot, colon, dash; max 160 chars).","Ensure QQ_DELIVERY_ID is not whitespace-only (it is cleaned/stripped)."],"exampleFix":"# before: GITHUB_RUN_ID unset and QQ_DELIVERY_ID unset\n# after:  QQ_DELIVERY_ID=manual-20260101-1","handlingStrategy":"validation","validationCode":"import os\ndelivery_id = (os.environ.get(\"QQ_DELIVERY_ID\") or os.environ.get(\"GITHUB_RUN_ID\") or \"\").strip()\nif not delivery_id:\n    raise SystemExit(\"set QQ_DELIVERY_ID or run under GITHUB_RUN_ID\")","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Run sends inside GitHub Actions so GITHUB_RUN_ID is always present.","For manual dispatches, set a unique QQ_DELIVERY_ID to preserve deduplication."],"tags":["config","github-actions","relay","validation"],"backgroundTag":null,"analyzedSha":"5ee1e990e73fbcae1969dc554be254fedb3ab888","analyzedAt":"2026-08-14T07:05:03.077Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}