DIYgod/RSSHub · error · Error
Douban 返回数据结构异常,可能触发反爬或限频。${details ? `上游信息:${details}` : ''
Error message
Douban 返回数据结构异常,可能触发反爬或限频。${details ? `上游信息:${details}` : ''} What it means
Thrown by the Douban TV coming-soon route when the API response's `subjects` field is not an array. The route calls Douban's Frodo API (`frodo.douban.com/api/v2/tv/coming_soon`) with HMAC-signed requests. When Douban's anti-crawler or rate-limiter kicks in, the API returns an error object (with `msg`/`message`/`reason` fields) instead of the expected `subjects` array. The error message appends any upstream detail from those fields.
Source
Thrown at lib/routes/douban/tv/coming.ts:188
url: apiUrl,
searchParams,
headers: {
Accept: 'application/json',
'User-Agent': apiClientUa,
},
});
return response.data as ComingSoonResponse;
} catch (error) {
throw buildFetchError(error);
}
},
config.cache.routeExpire,
false
)) as ComingSoonResponse;
if (!Array.isArray(data.subjects)) {
const details = data.msg || data.message || data.reason;
throw new Error(`Douban 返回数据结构异常,可能触发反爬或限频。${details ? `上游信息:${details}` : ''}`);
}
if (data.subjects.length === 0) {
throw new Error('Douban 返回空数据,可能触发反爬或限频。请稍后重试。');
}
const subscriptionCount = data.count ?? 0;
const total = data.total ?? 0;
const sortedSubjects = data.subjects.toSorted((a, b) => {
if (sortBy === 'time') {
const timeDiff = getSortTimestamp(a.pubdate) - getSortTimestamp(b.pubdate);
if (timeDiff !== 0) {
return timeDiff;
}
return getWishCount(b.wish_count) - getWishCount(a.wish_count);
}
const wishDiff = getWishCount(b.wish_count) - getWishCount(a.wish_count);
if (wishDiff !== 0) {
return wishDiff;View on GitHub (pinned to bed535e087)
Solutions
- Wait and retry after a cooldown period — the route uses cache so the first successful response serves subsequent requests.
- If the apiKey/apiSecret are stale, check for updates in the RSSHub repository.
- Route through a residential proxy or run from a non-datacenter IP if IP-level blocking is suspected.
- Inspect the upstream detail in the error message for the specific Douban error code/reason.
Defensive patterns
Strategy: retry
Validate before calling
// Pre-flight check: verify the API key is still valid by making a test request
async function checkDoubanApiHealth(): Promise<boolean> {
try {
const resp = await fetch(`${apiUrl}?count=1&apiKey=${apiKey}`, {
headers: { 'User-Agent': apiClientUa }
});
const data = await resp.json();
return Array.isArray(data.subjects);
} catch {
return false;
}
} Type guard
function isComingSoonResponse(data: unknown): data is { subjects: unknown[] } {
return typeof data === 'object' && data !== null && Array.isArray((data as any).subjects);
} Try / catch
try {
const feed = await fetch(`${rsshubUrl}/douban/tv/coming`);
} catch (e) {
if (e.message.includes('反爬或限频')) {
// Wait and retry — Douban rate limits are temporary
await new Promise(r => setTimeout(r, 60000));
return fetch(`${rsshubUrl}/douban/tv/coming`);
}
throw e;
} Prevention
- Poll the Douban route infrequently to avoid triggering rate limits.
- Rely on RSSHub's built-in cache rather than polling at high frequency.
- If self-hosting, consider routing through a residential proxy.
- Monitor the upstream detail in the error message for specific Douban error reasons.
When it happens
Trigger: Douban's Frodo API returns a rate-limit or anti-crawl error (e.g. {msg: 'rate limited', subjects: undefined}); the API key or signing algorithm has been deprecated/changed by Douban; the API endpoint structure has changed; the server IP is temporarily blocked.
Common situations: High request frequency triggers Douban rate limiting; the hardcoded apiKey/apiSecret has been revoked by Douban; RSSHub instance runs from a datacenter IP range that Douban blocks.
Related errors
- Douban 返回空数据,可能触发反爬或限频。请稍后重试。
- Invalid API response: ${JSON.stringify(response)}
- 对应 uid 的 Bilibili 用户 请求失败
- message ?? code
- response.message ?? `Error code ${response.code}`
AI-assisted analysis of DIYgod/RSSHub@bed535e087 (2026-08-12).
Data as JSON: /api/errors/5356418f487fc187.
Report an issue: GitHub.