{"record":{"id":"88efd8de280044c6","repo":"OtterMind/Chat2DB","slug":"delivery-is-still-in-progress","errorCode":null,"errorMessage":"delivery is still in progress","messagePattern":"delivery is still in progress","errorType":"http","errorClass":"RequestError","httpStatus":503,"severity":"warning","filePath":"script/github/qq_relay/relay_server.py","lineNumber":250,"sourceCode":"\n    def do_GET(self) -> None:  # noqa: N802\n        if self.path == \"/healthz\":\n            self._send_json(HTTPStatus.OK, {\"ok\": True})\n            return\n        self._send_json(HTTPStatus.NOT_FOUND, {\"error\": \"not found\"})\n\n    def do_POST(self) -> None:  # noqa: N802\n        delivery_id = \"\"\n        reserved = False\n        try:\n            if self.path != \"/v1/qq/github\":\n                raise RequestError(HTTPStatus.NOT_FOUND, \"not found\")\n            self._authorize()\n            delivery_id, message = self._validate_payload(self._read_payload())\n            existing = self.relay_state.deliveries.reserve(delivery_id)\n            if existing is not None:\n                if existing == \"pending\":\n                    raise RequestError(\n                        HTTPStatus.SERVICE_UNAVAILABLE, \"delivery is still in progress\"\n                    )\n                self._send_json(\n                    HTTPStatus.OK,\n                    {\n                        \"ok\": True,\n                        \"duplicate\": True,\n                        \"message_id\": existing,\n                    },\n                )\n                return\n            reserved = True\n            if not self.relay_state.rate_limiter.acquire():\n                raise RequestError(HTTPStatus.TOO_MANY_REQUESTS, \"rate limit exceeded\")\n            url_removed = False\n            try:\n                message_id = self.relay_state.onebot.send_group_message(message)\n            except OneBotRejected:","sourceCodeStart":232,"sourceCodeEnd":268,"githubUrl":"https://github.com/OtterMind/Chat2DB/blob/5ee1e990e73fbcae1969dc554be254fedb3ab888/script/github/qq_relay/relay_server.py#L232-L268","documentation":"Raised at relay_server.py:250 when DeliveryStore.reserve() returns an existing entry whose stored value is still 'pending' (relay_server.py:114). That means a prior request with the same delivery_id reserved the slot but never reached complete() or release(). Pending entries expire after the 24h TTL (DeliveryStore ttl_seconds=86400). Returned as HTTP 503, indicating a transient in-flight state, not a permanent failure.","triggerScenarios":"GitHub retries a delivery while the first one is still talking to OneBot (NapCat slow or hung up to the 15s urlopen timeout, relay_server.py:149); a duplicate event fired in quick succession before the first send finished.","commonSituations":"OneBot/NapCat is slow or unreachable so the first send blocks; aggressive webhook retry config re-fires before the in-flight send completes; relay restarted mid-send leaving a stale 'pending'.","solutions":["Retry the SAME delivery_id after a short backoff; dedup is idempotent.","Investigate OneBot latency/availability - the 503 is a symptom of the send not finishing.","Do NOT change the delivery_id to bypass it; a stale pending clears at the 24h TTL only."],"exampleFix":"# retry the same delivery_id with backoff on 503\nfor attempt in range(5):\n    resp = requests.post(url, json=payload)\n    if resp.status_code != 503:\n        break\n    time.sleep(2 ** attempt)","handlingStrategy":"retry","validationCode":"# nothing to validate pre-send; this is an in-flight server state.\n# only guard against it via bounded retry on HTTP 503 with the same delivery_id.","typeGuard":null,"tryCatchPattern":"for attempt in range(5):\n    resp = requests.post(url, json=payload)\n    if resp.status_code != 503:\n        break\n    time.sleep(min(2 ** attempt, 16))  # keep delivery_id stable","preventionTips":["Keep the delivery_id identical across retries so dedup still works.","Watch OneBot latency; a long-running first send is the usual root cause."],"tags":["http","retry","transient","qq-relay"],"backgroundTag":null,"analyzedSha":"5ee1e990e73fbcae1969dc554be254fedb3ab888","analyzedAt":"2026-08-14T07:05:03.077Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}