rust-lang/rust · error · anyhow::Error

cannot deserialize metrics from {url}

Error message

cannot deserialize metrics from {url}

What it means

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.

Source

Thrown at src/ci/citool/src/metrics.rs:101

            .and_then(|data| anyhow::Ok::<JsonRoot>(serde_json::from_str::<JsonRoot>(&data)?))
        {
            return Ok(metrics);
        }
    }

    let url = get_metrics_url(job_name, sha);
    let mut response = ureq::get(&url).call()?;
    if !response.status().is_success() {
        return Err(anyhow::anyhow!(
            "Cannot fetch metrics from {url}: {}\n{}",
            response.status(),
            response.body_mut().read_to_string()?
        ));
    }
    let data: JsonRoot = response
        .body_mut()
        .read_json()
        .with_context(|| anyhow::anyhow!("cannot deserialize metrics from {url}"))?;

    if let Ok(_) = std::fs::create_dir_all(cache_path.parent().unwrap()) {
        if let Ok(data) = serde_json::to_string(&data) {
            let _ = std::fs::write(cache_path, data);
        }
    }

    Ok(data)
}

fn get_metrics_url(job_name: &str, sha: &str) -> String {
    let suffix = if job_name.ends_with("-alt") { "-alt" } else { "" };
    format!("https://ci-artifacts.rust-lang.org/rustc-builds{suffix}/{sha}/metrics-{job_name}.json")
}

View on GitHub (pinned to 7088e4b63a)

Solutions

  1. Open the URL from the error in a browser and inspect the actual content to see how it deviates from expected JSON.
  2. Remove .citool-cache/<sha>/ to force a fresh download and rule out local cache corruption.
  3. If the upstream schema changed, update the JsonRoot struct in metrics.rs to match.
  4. If the payload is an HTML error page, treat it as a transient upstream issue and retry.

Example fix

null
Defensive patterns

Strategy: validation

Validate before calling

// Before deserializing, sanity-check the body looks like JSON.
let body = response.body_mut().read_to_string()?;
let trimmed = body.trim_start();
if !trimmed.starts_with('{') && !trimmed.starts_with('[') {
    eprintln!("Metrics payload at {url} is not JSON (got '{}'); skipping", &body[..body.len().min(80)]);
    return Ok(default);
}

Try / catch

let data: JsonRoot = match response.body_mut().read_json() {
    Ok(d) => d,
    Err(e) => {
        eprintln!("warning: cannot deserialize metrics from {url}: {e}");
        // fall back to empty/default metrics if acceptable for the caller
        JsonRoot::default()
    }
};

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of rust-lang/rust@7088e4b63a (2026-08-10). Data as JSON: /api/errors/75a64052dc59438f. Report an issue: GitHub.