DIYgod/RSSHub · error · Error
Cannot find n-token
Error message
Cannot find n-token
What it means
Thrown after the route fetches newrank.cn's bundled main.js and runs the regex /"N-Token":"([^"]+)/ against it. The N-Token is an anti-crawler header required by gw.newrank.cn's article-list API; if the regex finds no match, the script cannot proceed. This is a fragile scraping step that breaks whenever newrank renames, restructures, or removes the N-Token literal from its bundle.
Source
Thrown at lib/routes/newrank/wechat.ts:58
const { data: summaryHTML } = await got({
method: 'get',
url: `https://www.newrank.cn/new/readDetial?account=${uid}`,
headers: {
Connection: 'keep-alive',
Cookie: config.newrank.cookie,
},
});
const summary$ = load(summaryHTML);
const mainsrc = summary$('script')
.toArray()
.find((item) => (item.attribs.src || '').startsWith('/new/static/js/main.'))!.attribs.src;
const { data: mainScript } = await got({
method: 'get',
url: `https://www.newrank.cn${mainsrc}`,
});
const N_TOKEN_match = mainScript.match(/"N-Token":"([^"]+)/);
if (!N_TOKEN_match) {
throw new Error('Cannot find n-token');
}
const N_TOKEN = N_TOKEN_match[1];
const response = await got({
method: 'post',
url: 'https://gw.newrank.cn/api/wechat/xdnphb/detail/v1/rank/article/lists',
headers: {
Connection: 'keep-alive',
Cookie: config.newrank.cookie,
'n-token': N_TOKEN,
},
form: {
account: uid,
nonce,
xyz: utils.decrypt_wechat_detail_xyz(uid, nonce),
},
});
const name = response.data.value.user.name;View on GitHub (pinned to bed535e087)
Solutions
- Open the resolved mainsrc URL in a browser and grep for N-Token / n-token / token literals to find the new key, then update the regex.
- Check that config.newrank.cookie is valid — an expired cookie often makes newrank return a login page where the token never appears.
- If newrank moved the token into another chunk, follow the webpack chunk graph or load the page in Puppeteer and read the request header to capture N-Token at runtime.
- File an issue on the RSSHub route so the regex can be updated upstream.
Example fix
// before
const N_TOKEN_match = mainScript.match(/"N-Token":"([^"]+)/);
if (!N_TOKEN_match) {
throw new Error('Cannot find n-token');
}
// after — try multiple known shapes before failing
const N_TOKEN_match =
mainScript.match(/"N-Token":"([^"]+)/) ||
mainScript.match(/N-Token['"]?\s*[:=]\s*['"]([^"]+)/);
if (!N_TOKEN_match) {
throw new Error('Cannot find n-token (newrank bundle changed)');
} Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-flight: confirm the bundle still contains an N-Token literal before relying on it.
async function bundleHasNToken(mainsrc) {
const { data } = await got(`https://www.newrank.cn${mainsrc}`);
return /"N-Token":"[^"]+"/.test(data);
} Type guard
const hasNToken = (src: string): boolean => /"N-Token":"[^"]+"/.test(src);
Try / catch
// Try several known shapes, then fail with actionable context.
let N_TOKEN;
try {
const m = mainScript.match(/"N-Token":"([^"]+)/) || mainScript.match(/N-Token['"]?\s*[:=]\s*['"]([^"]+)/);
if (!m) throw new Error('no match');
N_TOKEN = m[1];
} catch (e) {
throw new Error('Cannot find n-token — newrank bundle likely changed; cookie may be expired');
} Prevention
- Treat the N-Token regex as fragile — wrap it and log the bundle hash so drift is detectable.
- Verify the cookie is fresh before debugging the regex; expired cookies often cause the bundle to omit the token.
- Consider capturing N-Token from a real request header via Puppeteer when static scraping breaks.
When it happens
Trigger: newrank ships a new JS bundle where the N-Token literal is split, minified under a different key, loaded dynamically, or removed entirely. The mainsrc selector (/new/static/js/main.) may still resolve, but the token string is no longer present in that file. Also possible if the got() request for the script is intercepted by a CDN/error page.
Common situations: newrank frontend deploy renames the token key; bundle chunking moves the token into a different chunk; the cookie is expired so newrank serves a login page instead of the app bundle; an anti-bot wall replaces the JS body.
Related errors
- WapHomeRenderData 数据未找到
- 无法获取最新的 year 和 issue
- Failed to extract posts from the Next.js RSC payload
- failed to parse AJAX response
- Failed to parse blogList from RSC data
AI-assisted analysis of DIYgod/RSSHub@bed535e087 (2026-08-12).
Data as JSON: /api/errors/0781acd2d67651ec.
Report an issue: GitHub.