jackwener/OpenCLI · error · CommandExecutionError

获取视频分P信息失败: ${payload?.message ?? 'unknown'} (${payload?.cod

Error message

获取视频分P信息失败: ${payload?.message ?? 'unknown'} (${payload?.code ?? 'malformed'})

What it means

loadSelectedPart calls the Bilibili web-interface/view API to fetch video metadata (including pages/分P list). After the HTTP call succeeds, it validates that the response is an object with code===0; anything else (error payload, non-zero code, non-object body) is rethrown as a CommandExecutionError embedding the API message and code. This distinguishes API-level rejection from transport errors, which are thrown by the preceding catch.

Source

Thrown at clis/bilibili/download.js:91

    }
    throw new CliError(
        'PAID_CONTENT',
        `该视频为付费内容(${PAYMENT_LABELS[paymentType]}),当前账号无观看权益,无法获取完整视频流`,
        '若已购买/已充电/已开通会员,加 --force 跳过本检查直接下载',
        EXIT_CODES.NOPERM,
    );
}

async function loadSelectedPart(page, bvid, pageNum) {
    let payload;
    try {
        payload = await apiGet(page, '/x/web-interface/view', { params: { bvid } });
    }
    catch (error) {
        throw new CommandExecutionError(`获取视频分P信息失败: ${error?.message || error}`);
    }
    if (!isObject(payload) || payload.code !== 0) {
        throw new CommandExecutionError(`获取视频分P信息失败: ${payload?.message ?? 'unknown'} (${payload?.code ?? 'malformed'})`);
    }
    return selectVideoPart(payload.data, pageNum);
}

cli({
    site: 'bilibili',
    name: 'download',
    access: 'read',
    description: '下载B站视频(需要 yt-dlp)',
    domain: 'www.bilibili.com',
    strategy: Strategy.COOKIE,
    args: [
        { name: 'bvid', required: true, positional: true, help: 'Video BV ID (e.g., BV1xxx)' },
        { name: 'output', default: './bilibili-downloads', help: 'Output directory' },
        { name: 'quality', default: 'best', help: 'Video quality (best, 1080p, 720p, 480p)' },
        { name: 'force', type: 'boolean', default: false, help: '跳过付费内容预检直接下载(已购买/已充电/已开通会员时用)' },
        { name: 'page', required: false, help: '分P 选集序号(从 1 开始)。多 P 视频下载该集;缺省下载默认 P1' },
    ],

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Verify the bvid is correct and the video is public (opens at bilibili.com/video/<bvid>).
  2. Retry later or from a cleaner IP if code is -352/-412 (risk control); use cookies of a logged-in session.
  3. If code is 'malformed', inspect the raw response — you are likely behind a proxy/firewall returning non-JSON.
  4. Handle the payload.code in calling code to give the end user a specific message (e.g. -404 → 'video not found').

Example fix

// before
const part = await loadSelectedPart(page, bvid, pageNum);
// after
let part;
try {
  part = await loadSelectedPart(page, bvid, pageNum);
} catch (e) {
  if (String(e.message).includes('-404')) throw new Error(`视频不存在: ${bvid}`);
  throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (!/^BV[0-9A-Za-z]{10}$/.test(bvid)) throw new Error(`invalid bvid: ${bvid}`);

Type guard

function isOkPayload(p) {
  return typeof p === 'object' && p !== null && p.code === 0 && 'data' in p;
}

Try / catch

try {
  const part = loadSelectedPart(page, bvid, pageNum);
} catch (e) {
  const m = String(e.message);
  if (m.includes('-404')) return null; // video gone
  if (m.includes('-352') || m.includes('-412')) return retryWithBackoff();
  throw e;
}

Prevention

When it happens

Trigger: Calling bilibili download with a bvid whose view API responds with code!==0 (e.g. -404 video not found, -400 invalid bvid, -352 risk-control) or with a body that is not a JSON object.

Common situations: Typo in the BV id, deleted/private/region-locked video, Bilibili risk control returning -352/-412, or a proxy/captive portal returning an HTML error page that fails object validation (code 'malformed').

Understand the failure class

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/df73ab0da8a357d5. Report an issue: GitHub.