{"record":{"id":"03afa8bb174e8990","repo":"BigPizzaV3/CodexPlusPlus","slug":"error-03afa8","errorCode":null,"errorMessage":"倍率接口返回了无效用户倍率","messagePattern":"倍率接口返回了无效用户倍率","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/codex-plus-core/src/sub2api.rs","lineNumber":121,"sourceCode":"fn 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 {\n            current = current.get(*key)?;\n        }\n        if let Some(message) = current\n            .as_str()\n            .map(str::trim)","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6/crates/codex-plus-core/src/sub2api.rs#L103-L139","documentation":"The same validator then checks the optional user_rate_multiplier with the identical finite-and-nonnegative rule. It fires only when the field is present (Some) but carries an invalid value, again guarding rate computations.","triggerScenarios":"上游响应中 user_rate_multiplier 存在但值为 NaN、无穷或负数。","commonSituations":"用户在 sub2api 后台手动输入了非法的用户倍率；上游对 null/缺失值的序列化 bug；管理员误配负数倍率。","solutions":["在 sub2api 后台修正该用户/密钥的自定义倍率为非负有限值","若不需要用户级倍率，删除该字段让 Option 为 None","升级上游修复非法值的序列化问题"],"exampleFix":"// before\nif let Some(value) = response.user_rate_multiplier {\n    if !value.is_finite() || value < 0.0 {\n        anyhow::bail!(\"倍率接口返回了无效用户倍率\");\n    }\n}\n// after\nif let Some(value) = response.user_rate_multiplier {\n    if !value.is_finite() || value < 0.0 {\n        tracing::warn!(\"忽略无效用户倍率: {value}，回退到 effective_rate_multiplier\");\n    }\n}","handlingStrategy":"try-catch","validationCode":"fn user_multiplier_sane(body: &serde_json::Value) -> bool {\n    match body.get(\"user_rate_multiplier\") {\n        None | Some(serde_json::Value::Null) => true,\n        Some(v) => v.as_f64().map(|x| x.is_finite() && x >= 0.0).unwrap_or(false),\n    }\n}","typeGuard":null,"tryCatchPattern":"match fetch_sub2api_billing_info(&profile).await {\n    Err(e) if e.to_string().contains(\"无效用户倍率\") => {\n        eprintln!(\"用户自定义倍率非法：{e}，已跳过，请修正 sub2api 用户倍率配置\");\n    }\n    other => other?,\n}","preventionTips":["后台设置用户倍率时只输入非负数字","不需要用户级倍率时留空而不是填 0 或负数","定期审计用户级倍率配置，防止误输入"],"tags":["validation","numeric","sub2api","billing"],"backgroundTag":"invalid-argument-value","analyzedSha":"b1ed92e5e4a2d74095d4b8db5af43cef7acba9c6","analyzedAt":"2026-09-19T23:35:21.129Z","contentChangedAt":"2026-09-19T23:35:21.129Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}