DIYgod/RSSHub · error · Error
Failed to solve the SafeLine challenge of weather.cma.cn
Error message
Failed to solve the SafeLine challenge of weather.cma.cn
What it means
weather.cma.cn is protected by the SafeLine WAF, which serves a JavaScript proof-of-work challenge: find a hex suffix so that SHA1(prefix+suffix) starts with `leading_zero_bit` zero bits. The handler brute-forces up to 100,000,000 suffixes; if none satisfies the difficulty within that cap it throws a generic Error. This is a runtime/anti-bot failure, not bad user input.
Source
Thrown at lib/routes/cma/channel.tsx:23
import { renderToString } from 'hono/jsx/dom/server';
import type { Language, Route } from '@/types';
import got from '@/utils/got';
import ofetch from '@/utils/ofetch';
import { parseDate } from '@/utils/parse-date';
import timezone from '@/utils/timezone';
const solveChallenge = (prefix: string, leadingZeroBits: number) => {
for (let count = 0; count < 100_000_000; count++) {
const suffix = count.toString(16);
const hash = createHash('sha1')
.update(prefix + suffix)
.digest();
if (hash.readUInt32BE(0) >>> (32 - leadingZeroBits) === 0) {
return suffix;
}
}
throw new Error('Failed to solve the SafeLine challenge of weather.cma.cn');
};
const fetchPageWithChallenge = async (url: string) => {
const response = await ofetch.raw<string>(url);
const html = response._data ?? '';
const challenge = response.headers
.getSetCookie()
.find((cookie) => cookie.startsWith('safeline_bot_challenge='))
?.split(';', 1)[0];
const prefix = html.match(/var prefix = '(\w+)';/)?.[1];
const leadingZeroBits = Number(html.match(/var leading_zero_bit = (\d+);/)?.[1]);
if (!challenge || !prefix || !leadingZeroBits) {
return html;
}
const answer = challenge.replace('safeline_bot_challenge=', 'safeline_bot_challenge_ans=') + solveChallenge(prefix, leadingZeroBits);
View on GitHub (pinned to bed535e087)
Solutions
- Retry the request — the challenge prefix and difficulty rotate, so a fresh page often solves quickly.
- If the regexes stopped matching, inspect the served HTML and update the two match patterns in solveChallenge's caller.
- Raise the iteration cap (the 100_000_000 literal) if difficulty is consistently high.
- Report an upstream SafeLine format change to the route maintainer.
Example fix
// before
// for (let count = 0; count < 100_000_000; count++) {
// after
// for (let count = 0; count < 500_000_000; count++) { Defensive patterns
Strategy: retry
Validate before calling
// No caller-side validation prevents this; the challenge is server-issued.
// Pre-flight: fetch the page once and confirm the two regexes match before solving.
const html = await ofetch.raw<string>(url);
const hasChallenge = /var prefix = '/.test(html._data ?? '') && /var leading_zero_bit = /.test(html._data ?? '');
if (!hasChallenge) { /* expect solve to fail; skip or alert */ } Try / catch
try {
await fetchPageWithChallenge(url);
} catch (e) {
// challenge difficulty/HTML may have rotated; one bounded retry often succeeds
await fetchPageWithChallenge(url);
} Prevention
- Treat SafeLine challenge failures as transient; retry once before surfacing.
- Monitor the two challenge regexes so an upstream format change is detected fast.
- Keep the iteration cap proportional to expected worst-case difficulty.
When it happens
Trigger: SafeLine raises `leading_zero_bit` so the 100M-iteration budget is exhausted; the page regexes (`var prefix = ...`, `var leading_zero_bit = ...`) no longer match because SafeLine changed its challenge HTML; the challenge cookie is stale.
Common situations: A SafeLine version bump changes the challenge page structure; a transient high-difficulty challenge; running on a slow CPU where the loop cannot complete in time.
Related errors
- 遇到源站风控校验,请稍后再试
- TechFlow API still returned an anti-crawler challenge after
- zhihu: failed to obtain a guest d_c0 cookie
- 日报数据不存在或为空
- Baidu security verification required. The cookie may be expi
AI-assisted analysis of DIYgod/RSSHub@bed535e087 (2026-08-12).
Data as JSON: /api/errors/9ddb27464b875376.
Report an issue: GitHub.