{"record":{"id":"1b60e874cb05030c","repo":"BigPizzaV3/CodexPlusPlus","slug":"error-1b60e8","errorCode":null,"errorMessage":"倍率接口返回了无效倍率","messagePattern":"倍率接口返回了无效倍率","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/codex-plus-core/src/sub2api.rs","lineNumber":116,"sourceCode":"        return format!(\"{cleaned}/sub2api/billing\");\n    }\n    format!(\"{cleaned}/v1/sub2api/billing\")\n}\n\nfn validate_sub2api_billing_response(response: &Sub2ApiBillingResponse) -> anyhow::Result<()> {\n    if response.object != \"sub2api.key_billing\"\n        || response.schema_version != 1\n        || response.billing_scope != \"token\"\n    {\n        anyhow::bail!(\"倍率接口返回结构不是 sub2api.key_billing\");\n    }\n    for value in [\n        response.group_rate_multiplier,\n        response.resolved_rate_multiplier,\n        response.effective_rate_multiplier,\n    ] {\n        if !value.is_finite() || value < 0.0 {\n            anyhow::bail!(\"倍率接口返回了无效倍率\");\n        }\n    }\n    if let Some(value) = response.user_rate_multiplier {\n        if !value.is_finite() || value < 0.0 {\n            anyhow::bail!(\"倍率接口返回了无效用户倍率\");\n        }\n    }\n    if response.observed_at.trim().is_empty() {\n        anyhow::bail!(\"倍率接口缺少观测时间\");\n    }\n    Ok(())\n}\n\nfn sub2api_error_message(body: &str) -> Option<String> {\n    let payload: Value = serde_json::from_str(body).ok()?;\n    for path in [&[\"error\", \"message\"][..], &[\"message\"][..], &[\"error\"][..]] {\n        let mut current = &payload;\n        for key in path {","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/f2074595a281bc057525c748175c8eb9805b0673/crates/codex-plus-core/src/sub2api.rs#L98-L134","documentation":"Thrown by validate_sub2api_billing_response when any of the three core multipliers (group, resolved, effective) is non-finite (NaN/inf) or negative. The offending input is the multiplier fields of the billing response; these values flow into cost calculations, so non-physical numbers are rejected rather than clamped.","triggerScenarios":"Thrown at crates/codex-plus-core/src/sub2api.rs:116 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["联系上游修正 sub2api 计费倍率配置","忽略计费展示直到上游修复"],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"f2074595a281bc057525c748175c8eb9805b0673","analyzedAt":"2026-08-23T12:52:24.489Z","contentChangedAt":"2026-08-23T12:52:24.489Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}