HKUDS/Vibe-Trading · error · RuntimeError
getuploadurl returned no upload URL (need upload_full_url or
Error message
getuploadurl returned no upload URL (need upload_full_url or upload_param): {upload_resp} What it means
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.
Source
Thrown at agent/src/channels/weixin.py:1394
file_key = os.urandom(16).hex()
upload_body: dict[str, Any] = {
"filekey": file_key,
"media_type": upload_type,
"to_user_id": to_user_id,
"rawsize": raw_size,
"rawfilemd5": raw_md5,
"filesize": padded_size,
"no_need_thumb": True,
"aeskey": aes_key_hex,
}
assert self._client is not None
upload_resp = await self._api_post("ilink/bot/getuploadurl", upload_body)
upload_full_url = str(upload_resp.get("upload_full_url", "") or "").strip()
upload_param = str(upload_resp.get("upload_param", "") or "")
if not upload_full_url and not upload_param:
raise RuntimeError(
"getuploadurl returned no upload URL "
f"(need upload_full_url or upload_param): {upload_resp}"
)
# Step 2: AES-128-ECB encrypt and POST to CDN
aes_key_b64 = base64.b64encode(aes_key_raw).decode()
encrypted_data = _encrypt_aes_ecb(raw_data, aes_key_b64)
if upload_full_url:
cdn_upload_url = upload_full_url
else:
cdn_upload_url = (
f"{self.config.cdn_base_url}/upload"
f"?encrypted_query_param={quote(upload_param)}"
f"&filekey={quote(file_key)}"
)
cdn_resp = await self._client.post(View on GitHub (pinned to 80ffdda44c)
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.
Example fix
// before
upload_resp = await self._api_post("ilink/bot/getuploadurl", upload_body)
// after
upload_resp = await self._api_post("ilink/bot/getuploadurl", upload_body)
if not (upload_resp.get("upload_full_url") or upload_resp.get("upload_param")):
self.logger.warning("getuploadurl refused: {}", upload_resp)
await asyncio.sleep(30) # retry / fall back to text message Defensive patterns
Strategy: retry
Try / catch
try:
await channel.send_media(chat_id, path)
except RuntimeError as e:
if "no upload URL" in str(e):
await asyncio.sleep(30)
await channel.send_media(chat_id, path)
else:
raise Prevention
- 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.
When it happens
Trigger: _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).
Common situations: 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.
Related errors
- WeChat send media error (ret={ret}, errcode={errcode}): {dat
- Failed to get QR code from WeChat API: {data}
- getUpdates failed: ret={ret} errcode={errcode} errmsg={data.
- WeChat send text error (ret={ret}, errcode={errcode}): {data
- Media file not found: {media_path}
AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28).
Data as JSON: /api/errors/37981859037c31dd.
Report an issue: GitHub.