jackwener/OpenCLI · error · CommandExecutionError
Bilibili ${label} API failed: ${message} (${payload.code})
Error message
Bilibili ${label} API failed: ${message} (${payload.code}) What it means
The Bilibili API returned valid JSON with a non-zero `code` that was NOT auth-like, so the library surfaces the API's own message and code as a CommandExecutionError. This is the generic 'API said no' path — the server processed the request but rejected it for a business reason.
Source
Thrown at clis/bilibili/utils.js:235
* `code` carries either an auth/permission failure (login expired, CSRF rejected,
* forbidden) or an application-level error (rate limit, validation, etc.). These
* two helpers route the envelope to the right typed error so every write adapter
* surfaces login problems as `AuthRequiredError`, not a generic execution error.
*/
export function isAuthLikeBilibiliError(code, message) {
return code === -101 || code === -111 || code === -403 || /csrf|登录|账号|权限|forbidden|permission|login/i.test(String(message ?? ''));
}
export function requireOkPayload(payload, label) {
if (!payload || typeof payload !== 'object' || Array.isArray(payload) || !Object.hasOwn(payload, 'code')) {
throw new CommandExecutionError(`Bilibili ${label} API returned a malformed payload`);
}
if (payload.code !== 0) {
const message = payload.message ?? 'unknown error';
if (isAuthLikeBilibiliError(payload.code, message)) {
throw new AuthRequiredError('bilibili.com', `Bilibili ${label} API requires login or permission: ${message} (${payload.code})`);
}
throw new CommandExecutionError(`Bilibili ${label} API failed: ${message} (${payload.code})`);
}
return payload.data;
}
/**
* POST form-encoded params to a Bilibili API endpoint.
* Runs inside the logged-in browser context and auto-attaches the bili_jct CSRF token,
* which Bilibili requires on every authenticated write request.
*/
export async function apiPost(page, path, opts = {}) {
const params = opts.params ?? {};
const stringified = Object.fromEntries(Object.entries(params).map(([k, v]) => [k, String(v)]));
const paramsJs = JSON.stringify(stringified);
const urlJs = JSON.stringify(`https://api.bilibili.com${path}`);
return page.evaluate(`
async () => {
const csrf = (document.cookie.match(/bili_jct=([^;]+)/) || [])[1] || "";
const body = new URLSearchParams(${paramsJs});View on GitHub (pinned to 49907e53dc)
Solutions
- Read the code/message in the error — Bilibili's codes are documented (e.g., -404 not found, 62002 hidden video) and indicate the exact problem.
- Verify the aid/bvid/params you passed are correct and the video still exists and is public.
- If you get risk-control codes (-509/-412), slow request rate, use cookies, or a residential IP.
- Retry with backoff only for transient codes; do not retry permanent codes like -400/-404.
Example fix
// before
try { await download(url); }
catch (e) { retry(url); } // retries even for -404
// after
try { await download(url); }
catch (e) {
if (!/-40[04]|62002/.test(e.message)) retry(url);
else console.error('Permanent API rejection:', e.message);
} Defensive patterns
Strategy: try-catch
Validate before calling
// preflight: check the video is public and exists
const v = await (await fetch(`https://api.bilibili.com/x/web-interface/view?bvid=${bvid}`)).json();
if (v.code === -404 || v.code === 62002) throw new Error(`Video unavailable: ${v.message}`); Try / catch
try { const data = requireOkPayload(payload, 'view'); } catch (e) { const m = /\((-[0-9]+)\)$/.exec(e.message); if (m && ['-400','-404','62002'].includes(m[1])) { /* permanent: don't retry */ } else if (m && ['-509','-412'].includes(m[1])) { await backoffAndRetry(); } else throw e; } Prevention
- Learn Bilibili's common codes: -400 bad request, -404 not found, 62002 hidden video, -509 risk control.
- Validate aid/bvid and endpoint params before sending.
- Rate-limit requests and use cookies/residential IPs to avoid risk-control codes.
- Only retry transient codes; fail fast on permanent ones.
When it happens
Trigger: payload.code !== 0 with codes like -400 (bad request), -404 (not found), 62002 (video invisible/hidden), -509 (risk control), 62012 (comments closed), etc., and the message doesn't match the auth regex.
Common situations: Malformed aid/bvid parameters; deleted or made-private videos; Bilibili risk-controlling datacenter IPs; passing wrong params to a specific endpoint (e.g., wrong search_type); region-locked content.
Related errors
- Cannot resolve aid for bvid: ${bvid}
- Bilibili view API returned a malformed payload during paid-c
- 获取视频分P信息失败: ${error?.message || error}
- 获取视频信息失败: ${err?.message || err}
- 获取视频信息失败: ${view?.message ?? 'unknown'} (${view?.code})
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/864b0b10cd477b28.
Report an issue: GitHub.