jackwener/OpenCLI · error · CliError
FORBIDDEN
FORBIDDEN
Error message
Gitee user page "${username}" is not accessible What it means
CliError with code FORBIDDEN thrown when the profile DOM snapshot reports blocked — the Gitee user page exists but its content is not accessible: private/restricted profile, suspended account, or an anti-bot/login wall served in place of the profile. The library raises FORBIDDEN with a hint that the profile may be private/restricted or the account unavailable.
Source
Thrown at clis/gitee/user.js:171
publicRepos,
giteeIndex,
};
})()
`);
const domSnapshotRecord = asRecord(rawDomSnapshot);
const domSnapshot = {
notFound: domSnapshotRecord?.notFound === true,
blocked: domSnapshotRecord?.blocked === true,
nickname: firstText(domSnapshotRecord?.nickname),
followers: normalizeCount(domSnapshotRecord?.followers),
publicRepos: normalizeCount(domSnapshotRecord?.publicRepos),
giteeIndex: normalizeCount(domSnapshotRecord?.giteeIndex),
};
if (domSnapshot.notFound) {
throw new CliError('NOT_FOUND', `Gitee user "${username}" does not exist`, 'Check the username and retry: opencli gitee user <username>');
}
if (domSnapshot.blocked) {
throw new CliError('FORBIDDEN', `Gitee user page "${username}" is not accessible`, 'The profile may be private/restricted, or the account may be unavailable');
}
const apiUrl = `${GITEE_USER_API}/${encodeURIComponent(username)}`;
const apiResponse = await fetch(apiUrl, {
headers: {
Accept: 'application/json',
'User-Agent': 'Mozilla/5.0',
Referer: profileUrl,
},
});
if (apiResponse.status === 404) {
throw new CliError('NOT_FOUND', `Gitee user "${username}" does not exist`, 'Check the username and retry: opencli gitee user <username>');
}
if (!apiResponse.ok) {
throw new CliError('REQUEST_FAILED', `Failed to read Gitee user profile API: ${apiResponse.status}`, 'Try again later or verify network access to gitee.com');
}
const apiUser = asRecord(await apiResponse.json());
const nickname = pickFirst(domSnapshot.nickname, firstText(apiUser?.name), firstText(apiUser?.login), username);
const followers = pickFirst(domSnapshot.followers, normalizeCount(apiUser?.followers), '-');View on GitHub (pinned to 49907e53dc)
Solutions
- Complete login via `opencli gitee auth` so the browser carries an authenticated session and retry
- Slow down request rate / use a residential IP to avoid Gitee risk-control challenges
- Verify in a normal browser whether the profile is private or the account suspended — if so, access is genuinely denied
- Catch code FORBIDDEN and treat as non-retryable in batch scripts
Example fix
// before opencli gitee user someprivateuser # FORBIDDEN: page not accessible // after opencli gitee auth # establish logged-in session opencli gitee user someprivateuser # retry with auth cookies
Defensive patterns
Strategy: try-catch
Validate before calling
// Check whether the profile page is publicly accessible before scraping
const res = await fetch(`https://gitee.com/${encodeURIComponent(username)}`);
if (res.status === 403 || (await res.text()).includes('verify')) {
console.error(`Access to "${username}" profile is restricted or challenged`);
process.exit(1);
} Type guard
function isForbiddenCode(e) {
return e instanceof Error && e.code === 'FORBIDDEN';
} Try / catch
try {
profile = await giteeUser(username);
} catch (e) {
if (e.code === 'FORBIDDEN') {
console.error(`Profile "${username}" is private/restricted or blocked — authenticate and retry`);
process.exit(e.exitCode ?? 1);
}
throw e;
} Prevention
- Establish a logged-in session (`opencli gitee auth`) before scraping profiles
- Throttle requests and use residential IPs to avoid risk-control challenges
- Treat FORBIDDEN as non-retryable for genuinely private accounts
- Detect captcha/verification pages early and surface a clear message
When it happens
Trigger: After page.goto(profileUrl), the snapshot's blocked flag is true — Gitee returned a verification/captcha page, a login-required wall, or an access-restricted profile instead of the public profile content.
Common situations: Gitee risk-control challenging the automated browser (captcha/slider) due to request rate or datacenter IP; viewing a private or suspended account; region-blocked content; session cookies flagged and access denied.
Related errors
- 12306 queryByTrainNo returned non-JSON body
- 12306 ${endpoint} returned non-JSON body
- Failed to parse 12306 station_name.js: source string not fou
- 请先在共享 Chrome 完成 1688 登录/验证,再重试(${action})
- Bilibili ${label} API returned a malformed payload
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/60d42251e28f949c.
Report an issue: GitHub.