NanmiCoder/MediaCrawler · critical · PlatformAccessError
XHS request blocked with HTTP {response.status_code}
Error message
XHS request blocked with HTTP {response.status_code} What it means
Raised by XiaoHongShuClient.request when xiaohongshu answers with HTTP 401, 403, or 429. These statuses mean the platform actively blocked the request: 401 invalid/expired session cookie, 403 forbidden (signature or cookie rejected), 429 rate limited. It is a PlatformAccessError, distinct from data errors, so callers can branch on access-level failures.
Source
Thrown at media_platform/xhs/client.py:154
Wrapper for httpx common request method, processes request response
Args:
method: Request method
url: Request URL
**kwargs: Other request parameters, such as headers, body, etc.
Returns:
"""
# Check if proxy is expired before each request
await self._refresh_proxy_if_expired()
# return response.text
return_response = kwargs.pop("return_response", False)
async with make_async_client(proxy=self.proxy) as client:
response = await client.request(method, url, timeout=self.timeout, **kwargs)
if response.status_code in {401, 403, 429}:
raise PlatformAccessError(
f"XHS request blocked with HTTP {response.status_code}"
)
if response.status_code == 471 or response.status_code == 461:
# someday someone maybe will bypass captcha
verify_type = response.headers["Verifytype"]
verify_uuid = response.headers["Verifyuuid"]
msg = f"CAPTCHA appeared, request failed, Verifytype: {verify_type}, Verifyuuid: {verify_uuid}, Response: {response}"
utils.logger.error(msg)
raise Exception(msg)
response_data: Optional[Dict] = None
try:
candidate_data = response.json()
if isinstance(candidate_data, dict):
response_data = candidate_data
except (TypeError, ValueError):
passView on GitHub (pinned to d6f7c5bb90)
Solutions
- On 401: re-login to xhs and refresh the cookie string in config (or use --lt cookie with a fresh export).
- On 403: update the signing implementation (xhshow) to the latest version - the site changed its signature algorithm.
- On 429: reduce request rate/concurrency and enable the proxy pool (ENABLE_IP_PROXY) with a valid provider.
- Catch PlatformAccessError at the crawler loop level to pause/backoff instead of hammering the endpoint.
Example fix
// before
# no handling around request()
// after
from media_platform.xhs.exception import PlatformAccessError
try:
res = await xhs_client.request("GET", uri, params=params)
except PlatformAccessError as e:
await asyncio.sleep(60) # backoff, then re-login/rotate proxy
raise Defensive patterns
Strategy: retry
Try / catch
from media_platform.xhs.exception import PlatformAccessError
try:
res = await xhs_client.request(method, uri, **kwargs)
except PlatformAccessError as e:
status = str(e).split()[-1]
if status == "401":
raise RuntimeError("xhs cookie expired - re-login required")
await asyncio.sleep(backoff_for(status)) # 403/429: backoff + rotate proxy
raise Prevention
- Monitor cookie age and refresh before expiry on long runs.
- Rate-limit xhs requests below the 429 threshold.
- Keep the xhshow signer updated when xiaohongshu rotates its signature scheme.
- Branch on the status embedded in the message: 401=cookie, 403=signature/blocked, 429=rate.
When it happens
Trigger: Any signed xhs API call (search, note detail, comments, creator notes) made with an expired web_session cookie, an x-s/x-t signature that no longer matches xiaohongshu's algorithm version, or a request burst that trips 429.
Common situations: Long crawls where the cookie expired mid-run; xiaohongshu updating their signing scheme so the local signer produces rejected x-s values; too-high concurrency or missing ENABLE_PROXY_IP causing rate limits.
Related errors
- CAPTCHA appeared, request failed, Verifytype: {verify_type},
- Request failed, method: {method}, url: {url}, status code: {
- get weibo detail err: {response.text}
- params or payload is required
- 300012
AI-assisted analysis of NanmiCoder/MediaCrawler@d6f7c5bb90 (2026-08-15).
Data as JSON: /api/errors/071fea002ab48451.
Report an issue: GitHub.