{"record":{"id":"75a64052dc59438f","repo":"rust-lang/rust","slug":"cannot-deserialize-metrics-from-url","errorCode":null,"errorMessage":"cannot deserialize metrics from {url}","messagePattern":"cannot deserialize metrics from (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src/ci/citool/src/metrics.rs","lineNumber":101,"sourceCode":"            .and_then(|data| anyhow::Ok::<JsonRoot>(serde_json::from_str::<JsonRoot>(&data)?))\n        {\n            return Ok(metrics);\n        }\n    }\n\n    let url = get_metrics_url(job_name, sha);\n    let mut response = ureq::get(&url).call()?;\n    if !response.status().is_success() {\n        return Err(anyhow::anyhow!(\n            \"Cannot fetch metrics from {url}: {}\\n{}\",\n            response.status(),\n            response.body_mut().read_to_string()?\n        ));\n    }\n    let data: JsonRoot = response\n        .body_mut()\n        .read_json()\n        .with_context(|| anyhow::anyhow!(\"cannot deserialize metrics from {url}\"))?;\n\n    if let Ok(_) = std::fs::create_dir_all(cache_path.parent().unwrap()) {\n        if let Ok(data) = serde_json::to_string(&data) {\n            let _ = std::fs::write(cache_path, data);\n        }\n    }\n\n    Ok(data)\n}\n\nfn get_metrics_url(job_name: &str, sha: &str) -> String {\n    let suffix = if job_name.ends_with(\"-alt\") { \"-alt\" } else { \"\" };\n    format!(\"https://ci-artifacts.rust-lang.org/rustc-builds{suffix}/{sha}/metrics-{job_name}.json\")\n}\n","sourceCodeStart":83,"sourceCodeEnd":116,"githubUrl":"https://github.com/rust-lang/rust/blob/7088e4b63a9516ebfbfe2ab2d999cf01a528ac14/src/ci/citool/src/metrics.rs#L83-L116","documentation":"Thrown by download_job_metrics when the HTTP response body cannot be deserialized into the expected JsonRoot structure (read_json fails). The URL is included so the caller can fetch and inspect the malformed payload. This indicates the artifact was served (status was 2xx) but its content does not match the metrics JSON schema.","triggerScenarios":"The metrics endpoint returns an HTML error page or partial JSON (truncated upload); the JSON schema changed and citool's JsonRoot struct is out of date; a cached file in .citool-cache was corrupted on disk.","commonSituations":"ci-artifacts served a truncated file due to an interrupted upload; version skew between the publishing tool and citool's deserialization struct; disk corruption of the local cache file.","solutions":["Open the URL from the error in a browser and inspect the actual content to see how it deviates from expected JSON.","Remove .citool-cache/<sha>/ to force a fresh download and rule out local cache corruption.","If the upstream schema changed, update the JsonRoot struct in metrics.rs to match.","If the payload is an HTML error page, treat it as a transient upstream issue and retry."],"exampleFix":"null","handlingStrategy":"validation","validationCode":"// Before deserializing, sanity-check the body looks like JSON.\nlet body = response.body_mut().read_to_string()?;\nlet trimmed = body.trim_start();\nif !trimmed.starts_with('{') && !trimmed.starts_with('[') {\n    eprintln!(\"Metrics payload at {url} is not JSON (got '{}'); skipping\", &body[..body.len().min(80)]);\n    return Ok(default);\n}","typeGuard":null,"tryCatchPattern":"let data: JsonRoot = match response.body_mut().read_json() {\n    Ok(d) => d,\n    Err(e) => {\n        eprintln!(\"warning: cannot deserialize metrics from {url}: {e}\");\n        // fall back to empty/default metrics if acceptable for the caller\n        JsonRoot::default()\n    }\n};","preventionTips":["Pin citool and the publishing tool to compatible versions so JsonRoot stays in sync.","Clear .citool-cache when switching branches to avoid stale/corrupt local files.","Log the raw payload length when deserialization fails to spot truncation."],"tags":["ci","citool","metrics","serialization","json"],"backgroundTag":null,"analyzedSha":"7088e4b63a9516ebfbfe2ab2d999cf01a528ac14","analyzedAt":"2026-08-10T14:17:03.603Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}