{"record":{"id":"37981859037c31dd","repo":"HKUDS/Vibe-Trading","slug":"getuploadurl-returned-no-upload-url-need-upload-f","errorCode":null,"errorMessage":"getuploadurl returned no upload URL (need upload_full_url or upload_param): {upload_resp}","messagePattern":"getuploadurl returned no upload URL \\(need upload_full_url or upload_param\\): (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"agent/src/channels/weixin.py","lineNumber":1394,"sourceCode":"        file_key = os.urandom(16).hex()\n        upload_body: dict[str, Any] = {\n            \"filekey\": file_key,\n            \"media_type\": upload_type,\n            \"to_user_id\": to_user_id,\n            \"rawsize\": raw_size,\n            \"rawfilemd5\": raw_md5,\n            \"filesize\": padded_size,\n            \"no_need_thumb\": True,\n            \"aeskey\": aes_key_hex,\n        }\n\n        assert self._client is not None\n        upload_resp = await self._api_post(\"ilink/bot/getuploadurl\", upload_body)\n\n        upload_full_url = str(upload_resp.get(\"upload_full_url\", \"\") or \"\").strip()\n        upload_param = str(upload_resp.get(\"upload_param\", \"\") or \"\")\n        if not upload_full_url and not upload_param:\n            raise RuntimeError(\n                \"getuploadurl returned no upload URL \"\n                f\"(need upload_full_url or upload_param): {upload_resp}\"\n            )\n\n        # Step 2: AES-128-ECB encrypt and POST to CDN\n        aes_key_b64 = base64.b64encode(aes_key_raw).decode()\n        encrypted_data = _encrypt_aes_ecb(raw_data, aes_key_b64)\n\n        if upload_full_url:\n            cdn_upload_url = upload_full_url\n        else:\n            cdn_upload_url = (\n                f\"{self.config.cdn_base_url}/upload\"\n                f\"?encrypted_query_param={quote(upload_param)}\"\n                f\"&filekey={quote(file_key)}\"\n            )\n\n        cdn_resp = await self._client.post(","sourceCodeStart":1376,"sourceCodeEnd":1412,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/channels/weixin.py#L1376-L1412","documentation":"During media upload, the channel calls ilink/bot/getuploadurl and needs either upload_full_url or upload_param to know where to PUT the encrypted bytes. If both are absent/empty in the response, it raises RuntimeError embedding the full response, since the upload cannot proceed without a target URL.","triggerScenarios":"_send_media_file gets a getuploadurl response where upload_full_url is empty/whitespace and upload_param is empty — server refused to allocate an upload slot (auth, quota, or unsupported media type).","commonSituations":"WeChat CDN upload quota exhausted for the bot account; session/token degraded so the endpoint returns an error object instead of upload fields; media type or file metadata in upload_body not accepted by the server; API schema change renaming the fields.","solutions":["Inspect upload_resp in the message: an errcode/ret present indicates auth or quota issues to fix first.","Retry after a delay if quota-related; media upload slots are often rate-limited.","Re-authenticate if the token is stale (session pause can degrade subsequent calls).","Verify the upload_body media type/size fields match supported CDN media kinds."],"exampleFix":"// before\nupload_resp = await self._api_post(\"ilink/bot/getuploadurl\", upload_body)\n\n// after\nupload_resp = await self._api_post(\"ilink/bot/getuploadurl\", upload_body)\nif not (upload_resp.get(\"upload_full_url\") or upload_resp.get(\"upload_param\")):\n    self.logger.warning(\"getuploadurl refused: {}\", upload_resp)\n    await asyncio.sleep(30)  # retry / fall back to text message","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    await channel.send_media(chat_id, path)\nexcept RuntimeError as e:\n    if \"no upload URL\" in str(e):\n        await asyncio.sleep(30)\n        await channel.send_media(chat_id, path)\n    else:\n        raise","preventionTips":["Throttle media uploads to avoid CDN slot quota exhaustion.","Keep the session authenticated so getuploadurl returns valid fields.","Inspect the embedded upload_resp for errcode clues."],"tags":["wechat","media-upload","cdn","api-error"],"backgroundTag":"upload-url-not-provided","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}