{"record":{"id":"3c7563fa14279117","repo":"jackwener/OpenCLI","slug":"bilibili-creator-comparison-returned-malformed-met","errorCode":null,"errorMessage":"Bilibili creator comparison returned malformed metric ${definition.key}","messagePattern":"Bilibili creator comparison returned malformed metric (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/bilibili/creator-stats.js","lineNumber":125,"sourceCode":"            '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)) {\n        throw new CommandExecutionError(`Bilibili creator comparison returned non-integer metric ${definition.key}`);\n    }\n    return definition.divisor ? raw / definition.divisor : raw;\n}\n\ncli({\n    site: 'bilibili',\n    name: 'creator-stats',\n    description: '读取当前账号最近稿件的核心创作指标（需登录创作中心）',\n    access: 'read',\n    example: 'opencli bilibili creator-stats <bvid-or-video-url> -f json',\n    domain: 'member.bilibili.com',\n    strategy: Strategy.COOKIE,","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/bilibili/creator-stats.js#L107-L143","documentation":"metricValue found the metric key but its value is not a usable non-negative finite number (string, boolean, NaN, Infinity, or negative). The library whitelists fields from an undocumented endpoint and hard-validates each value, so corrupt or type-changed data aborts the command instead of producing bogus statistics.","triggerScenarios":"target.stat[key] (or the row's duration field) holds a non-number: a string like \"1,234\" after a Bilibili format change, a float for count metrics, negative counters, or null-adjacent sentinel values other than null.","commonSituations":"Bilibili changes a field from number to string with formatting; negative corrections make counters negative; floating-point creeping into integer counters; proxies or caches corrupting the payload.","solutions":["Retry the command; transient partial writes on Bilibili's side can resolve.","Inspect the raw compare-endpoint response to see the actual value/type for the failing key.","If Bilibili changed the type, update the CLI to coerce/parse the new representation (e.g. strip commas from strings).","Pin/update the CLI version whose validators match the current API."],"exampleFix":"// caller-side tolerant extraction\nconst raw = target.stat?.[def.key];\nconst value = typeof raw === 'string' ? Number(raw.replace(/,/g, '')) : raw;\nconst stat = (typeof value === 'number' && Number.isFinite(value) && value >= 0) ? value : null;","handlingStrategy":"type-guard","validationCode":"const raw = row?.stat?.[def.key];\nif (!(raw === null || (typeof raw === 'number' && Number.isFinite(raw) && raw >= 0))) { /* treat as unavailable */ }","typeGuard":"function isNonNegativeFinite(v) {\n    return typeof v === 'number' && Number.isFinite(v) && v >= 0;\n}","tryCatchPattern":"try {\n    const rows = await creatorStats(bvid);\n} catch (e) {\n    if (/malformed metric/.test(e.message)) {\n        // retry once, then fall back to null metrics\n    } else throw e;\n}","preventionTips":["Retry transient corrupt payloads before giving up","Validate API payloads at the boundary in your own tooling too","Update CLI promptly when Bilibili changes field types","Avoid parsing cached/stale responses through modifying proxies"],"tags":["bilibili","validation","type-mismatch","malformed-response"],"backgroundTag":"schema-validation-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}