{"record":{"id":"fcffaebb5668b585","repo":"jackwener/OpenCLI","slug":"bilibili-creator-comparison-returned-malformed-sta","errorCode":null,"errorMessage":"Bilibili creator comparison returned malformed stat data for ${bvid}","messagePattern":"Bilibili creator comparison returned malformed stat data for (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/bilibili/creator-stats.js","lineNumber":112,"sourceCode":"    const matches = [];\n    for (const item of list) {\n        if (!isRecord(item) || typeof item.bvid !== 'string' || !/^BV[0-9A-Za-z]{10}$/.test(item.bvid)) {\n            throw new CommandExecutionError('Bilibili creator comparison returned a malformed manuscript row');\n        }\n        if (item.bvid === bvid) matches.push(item);\n    }\n    if (matches.length > 1) {\n        throw new CommandExecutionError(`Bilibili creator comparison returned duplicate rows for ${bvid}`);\n    }\n    if (matches.length === 0) {\n        throw new EmptyResultError(\n            `bilibili creator-stats ${bvid}`,\n            'The manuscript was not present in the latest 100 creator analytics rows; it may be older, not owned by this account, or not analyzed yet.',\n        );\n    }\n    const target = matches[0];\n    if (!isRecord(target.stat)) {\n        throw new CommandExecutionError(`Bilibili creator comparison returned malformed stat data for ${bvid}`);\n    }\n    return target;\n}\n\nfunction metricValue(target, definition) {\n    const source = definition.source === 'target' ? target : target.stat;\n    if (!Object.prototype.hasOwnProperty.call(source, definition.key)) {\n        throw new CommandExecutionError(`Bilibili creator comparison omitted metric ${definition.key}`);\n    }\n    const raw = source[definition.key];\n    if (raw === null) return null;\n    if (typeof raw !== 'number' || !Number.isFinite(raw) || raw < 0) {\n        throw new CommandExecutionError(`Bilibili creator comparison returned malformed metric ${definition.key}`);\n    }\n    if (definition.unit === 'percent' && raw > 10_000) {\n        throw new CommandExecutionError(`Bilibili creator comparison returned out-of-range percentage ${definition.key}`);\n    }\n    if ((definition.unit === 'count' || definition.unit === 'seconds') && !Number.isSafeInteger(raw)) {","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/bilibili/creator-stats.js#L94-L130","documentation":"selectTarget found the requested bvid among the creator-comparison rows, but the matched row's `stat` field is not a plain object (missing, null, or an array). Because the command reads every metric out of `target.stat`, it refuses to continue rather than fail later per-metric. This guards against undocumented contract drift in Bilibili's member.bilibili.com comparison endpoint.","triggerScenarios":"Bilibili's /x/web/data/archive_diagnose/compare?size=100 returns a row for the bvid whose `stat` field is absent, null, or a non-object (e.g. API schema change, partial analytics data, or the row being a special entry type without stats).","commonSituations":"Bilibili silently changes the undocumented creator-center response shape; a newly published manuscript gets a row before analytics are generated; proxy/CDN returns partially stripped JSON; account has an unusual archive type (charging-exclusive, course) whose rows lack `stat`.","solutions":["Re-run the command after a few minutes to let Bilibili finish generating analytics for the manuscript.","Re-login to the creator center so the session/cookie matches a healthy member.bilibili.com API response.","Check the archive type of the bvid; if it is charging-exclusive or a course, the comparison endpoint may not carry stats for it.","If reproducible, update the CLI's response parsing to the new API shape (verify with a raw request to the compare endpoint)."],"exampleFix":"// caller-side guard before relying on stat\nconst row = await selectTarget(list, bvid);\nif (!row || typeof row !== 'object' || Array.isArray(row) || !row.stat || typeof row.stat !== 'object' || Array.isArray(row.stat)) {\n    // fall back: skip stats for this bvid or retry later\n}","handlingStrategy":"type-guard","validationCode":"const isRecord = (v) => Boolean(v) && typeof v === 'object' && !Array.isArray(v);\nconst row = list.find((item) => item?.bvid === bvid);\nif (!isRecord(row?.stat)) { /* defer stats / retry later */ }","typeGuard":"const isRecord = (v) => Boolean(v) && typeof v === 'object' && !Array.isArray(v);\nfunction hasStat(row) { return isRecord(row) && isRecord(row.stat); }","tryCatchPattern":"try {\n    const stats = await creatorStats(bvid);\n} catch (e) {\n    if (/malformed stat data/.test(e.message)) {\n        // treat as no-analytics-yet: retry later or skip metrics\n    } else throw e;\n}","preventionTips":["Only query manuscripts that already show analytics in the creator-center web UI","Re-login periodically to keep member.bilibili.com sessions healthy","Watch for Bilibili creator-center API changes before bulk querying","Handle per-video failures gracefully instead of failing whole batches"],"tags":["bilibili","api-schema","validation","malformed-response"],"backgroundTag":"malformed-api-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}