diff --git a/gateway/platforms/weixin.py b/gateway/platforms/weixin.py index 36bb3dd21c2c9..73e9e68ea70f6 100644 --- a/gateway/platforms/weixin.py +++ b/gateway/platforms/weixin.py @@ -378,12 +378,16 @@ async def _api_post( ) -> Dict[str, Any]: body = _json_dumps({**payload, "base_info": _base_info()}) url = f"{base_url.rstrip('/')}/{endpoint}" - timeout = aiohttp.ClientTimeout(total=timeout_ms / 1000) - async with session.post(url, data=body, headers=_headers(token, body), timeout=timeout) as response: - raw = await response.text() - if not response.ok: - raise RuntimeError(f"iLink POST {endpoint} HTTP {response.status}: {raw[:200]}") - return json.loads(raw) + # Use asyncio.wait_for() instead of aiohttp ClientTimeout to avoid + # "Timeout context manager should be used inside a task" errors when + # invoked via asyncio.run_coroutine_threadsafe() from cron jobs. + async def _do() -> Dict[str, Any]: + async with session.post(url, data=body, headers=_headers(token, body)) as response: + raw = await response.text() + if not response.ok: + raise RuntimeError(f"iLink POST {endpoint} HTTP {response.status}: {raw[:200]}") + return json.loads(raw) + return await asyncio.wait_for(_do(), timeout=timeout_ms / 1000) async def _api_get( @@ -398,12 +402,16 @@ async def _api_get( "iLink-App-Id": ILINK_APP_ID, "iLink-App-ClientVersion": str(ILINK_APP_CLIENT_VERSION), } - timeout = aiohttp.ClientTimeout(total=timeout_ms / 1000) - async with session.get(url, headers=headers, timeout=timeout) as response: - raw = await response.text() - if not response.ok: - raise RuntimeError(f"iLink GET {endpoint} HTTP {response.status}: {raw[:200]}") - return json.loads(raw) + # Use asyncio.wait_for() instead of aiohttp ClientTimeout to avoid + # "Timeout context manager should be used inside a task" errors when + # invoked via asyncio.run_coroutine_threadsafe() from cron jobs. + async def _do() -> Dict[str, Any]: + async with session.get(url, headers=headers) as response: + raw = await response.text() + if not response.ok: + raise RuntimeError(f"iLink GET {endpoint} HTTP {response.status}: {raw[:200]}") + return json.loads(raw) + return await asyncio.wait_for(_do(), timeout=timeout_ms / 1000) async def _get_updates(