jackwener/OpenCLI · error · CommandExecutionError
Zhihu answer navigation changed identity for answer ${target
Error message
Zhihu answer navigation changed identity for answer ${target.answerId} What it means
After page.goto, extractAnswer reads the current URL and re-parses it with parseAnswerTarget to verify Zhihu actually landed on the requested answer. If the parsed URL lacks an answerId, has a different answerId, has no questionId, or the questionId mismatches the requested target, this error is thrown. It guards against Zhihu silently redirecting to a login page, captcha, question page, or a different answer, which would otherwise produce scraped data for the wrong content.
Source
Thrown at clis/zhihu/download-helpers.js:159
});
return requireArticle(raw);
}
export async function extractAnswer(page, target) {
try {
await page.goto(`https://www.zhihu.com/answer/${target.answerId}`);
}
catch (error) {
throw new CommandExecutionError(
`Failed to open Zhihu answer ${target.answerId}: ${error instanceof Error ? error.message : String(error)}`,
'Open the answer URL in Chrome and retry after the page is reachable.',
);
}
const currentUrl = typeof page.getCurrentUrl === 'function' ? await page.getCurrentUrl().catch(() => '') : '';
const currentTarget = parseAnswerTarget(currentUrl);
if (!currentTarget || currentTarget.answerId !== target.answerId || !currentTarget.questionId
|| (target.questionId && currentTarget.questionId && target.questionId !== currentTarget.questionId)) {
throw new CommandExecutionError(`Zhihu answer navigation changed identity for answer ${target.answerId}`);
}
const apiUrl = `https://www.zhihu.com/api/v4/answers/${target.answerId}?include=content,author,created_time,question,url`;
const normalize = `(${normalizeContentImages.toString()})`;
const raw = await page.evaluate(`
(async () => {
let response;
try {
response = await fetch(${JSON.stringify(apiUrl)}, { credentials: 'include' });
} catch (error) {
return { fetchError: error instanceof Error ? error.message : String(error) };
}
let payload;
try {
payload = await response.json();
} catch {
return { status: response.status, malformed: true };
}View on GitHub (pinned to 49907e53dc)
Solutions
- Log in to www.zhihu.com in the connected Chrome profile so navigation is not redirected to sign-in.
- Open the answer URL manually and check whether it was deleted or redirects; use the current canonical URL to rebuild the target.
- Print/inspect page.getCurrentUrl() after the failure to see where the navigation actually landed.
- Retry later if a captcha/risk-control wall redirected you; verify the answerId/questionId target values are correct and current.
Example fix
// before
await extractAnswer(page, { answerId: oldId, questionId: staleQuestionId });
// after
// confirm the canonical URL first, then use matching ids
const canonical = 'https://www.zhihu.com/question/999/answer/12345';
const t = parseDownloadTarget(canonical); // { kind:'answer', answerId:'12345', questionId:'999' }
await extractAnswer(page, t); Defensive patterns
Strategy: validation
Validate before calling
const target = parseDownloadTarget(input);
if (!target || target.kind !== 'answer' || !/^\d+$/.test(target.answerId)) {
throw new Error('invalid answer target');
}
// Prefer a fresh canonical URL from Zhihu over a stale stored questionId Type guard
function isAnswerTarget(t) {
return !!t && typeof t === 'object' && t.kind === 'answer'
&& typeof t.answerId === 'string' && /^\d+$/.test(t.answerId)
&& (t.questionId === undefined || /^\d*$/.test(t.questionId || ''));
} Try / catch
try {
await extractAnswer(page, target);
} catch (err) {
if (String(err.message).includes('changed identity')) {
// likely login/captcha redirect or deleted answer — inspect actual URL
const landed = await page.getCurrentUrl();
console.error('redirected to:', landed, '- log in or refresh the target');
}
throw err;
} Prevention
- Log in to Zhihu in the connected Chrome profile before extracting answers
- Refresh stored answer targets from canonical URLs; old questionId mappings can go stale
- Check for deleted/collapsed answers before batch runs
- Wait out captcha/risk-control walls instead of retrying immediately
When it happens
Trigger: Calling extractAnswer when, after navigation, the final URL is not /question/<qid>/answer/<aid>: redirect to sign-in/captcha wall, answer deleted or collapsed redirecting to the question page, Zhihu redirecting to a normalized/different answer URL, or the bridge driver not exposing getCurrentUrl (empty string fails the check).
Common situations: Not logged in and Zhihu forces a login redirect; anti-bot risk control redirects to a verification page; the answer was deleted so Zhihu bounces to the question; a mobile-app-style link redirects to a different canonical answer; target.questionId came from an outdated URL that no longer matches after Zhihu re-mapped the question.
Related errors
- zhihu collections
- ACTION_NOT_AVAILABLE
- Amazon review discussion requires an active signed-in Amazon
- ${probe.detail}
- Could not extract CSRF token from barchart.com. Make sure yo
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/309741c5a509f544.
Report an issue: GitHub.