{"record":{"id":"86d929a4e5838515","repo":"denoland/deno","slug":"failed-to-parse-profile","errorCode":null,"errorMessage":"Failed to parse profile: {}","messagePattern":"Failed to parse profile: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"runtime/cpu_profiler/cpuprof.rs","lineNumber":67,"sourceCode":"struct FunctionStats {\n  function_name: String,\n  url: String,\n  line_number: i32,\n  self_time: i64,\n  total_time: i64,\n  self_samples: i32,\n  total_samples: i32,\n}\n\npub(crate) fn generate_markdown_report(\n  profile: &serde_json::Value,\n  filepath: &std::path::Path,\n  interval_us: i64,\n) -> std::io::Result<()> {\n  let profile: CpuProfile = match serde_json::from_value(profile.clone()) {\n    Ok(p) => p,\n    Err(err) => {\n      return Err(std::io::Error::new(\n        std::io::ErrorKind::InvalidData,\n        format!(\"Failed to parse profile: {}\", err),\n      ));\n    }\n  };\n\n  let mut md = String::new();\n\n  // Calculate stats\n  let duration_us = profile.end_time - profile.start_time;\n  let duration_ms = duration_us as f64 / 1000.0;\n  let total_samples = profile.samples.len();\n  let total_functions = profile.nodes.len();\n\n  // Build node map\n  let node_map: HashMap<i32, &ProfileNode> =\n    profile.nodes.iter().map(|n| (n.id, n)).collect();\n","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/runtime/cpu_profiler/cpuprof.rs#L49-L85","documentation":"When the CPU profiler writes its report (enabled via the `--cpu-profiler` + markdown-report option in runtime/cpu_profiler), `generate_markdown_report` deserializes the V8 CPU profile JSON into the strict `CpuProfile` struct (nodes, samples, timeDeltas, startTime/endTime, ...). If serde rejects the value, the error is wrapped into `std::io::Error` with kind `InvalidData` and this message including the serde detail. The raw `.cpuprofile` file has already been written; only the `.md` report fails, and runtime/cpu_profiler/mod.rs logs the error and continues.","triggerScenarios":"Running with the CPU profiler and markdown-report generation enabled where the profile JSON does not match the `CpuProfile` schema: missing keys (e.g. no `timeDeltas`), wrong value types, or extra-incompatible shapes after a V8 version bumps the profile format (e.g. new node fields or changed sample encoding).","commonSituations":"Upgrading V8/deno_core where the `.cpuprofile` format evolved; a profile with zero samples producing surprising shapes; feeding a hand-edited or external (Chrome DevTools-style, different version) profile file into the report generator.","solutions":["Check the appended serde error (`Failed to parse profile: {err}`) — it names the exact field and reason that failed to deserialize.","Align the `CpuProfile` struct fields in runtime/cpu_profiler/cpuprof.rs with the actual JSON (add `#[serde(default)]` for newly optional fields or map renamed keys with `#[serde(rename = \"...\")]`).","If the format changed with a V8 upgrade, update the deserialization for the new format rather than skipping the report.","The `.cpuprofile` itself is still valid — as a workaround, open it directly in Chrome DevTools > Performance or speedscope until the report generator is fixed."],"exampleFix":"// before (runtime/cpu_profiler/cpuprof.rs)\n#[derive(Deserialize)]\npub(crate) struct CpuProfile {\n  pub nodes: Vec<ProfileNode>,\n  pub samples: Vec<i32>,\n  pub time_deltas: Vec<i32>,\n  // ...\n}\n\n// after — tolerate fields V8 makes optional\n#[derive(Deserialize)]\npub(crate) struct CpuProfile {\n  #[serde(default)]\n  pub nodes: Vec<ProfileNode>,\n  #[serde(default)]\n  pub samples: Vec<i32>,\n  #[serde(default)]\n  pub time_deltas: Vec<i32>,\n  // ...\n}","handlingStrategy":"try-catch","validationCode":"// If you call generate_markdown_report on an arbitrary profile value,\n// pre-check the shape before deserializing:\nfn looks_like_cpuprofile(v: &serde_json::Value) -> bool {\n  v.get(\"nodes\").map(|n| n.is_array()).unwrap_or(false)\n    && v.get(\"samples\").map(|n| n.is_array()).unwrap_or(false)\n    && v.get(\"startTime\").map(|n| n.is_i64()).unwrap_or(false)\n    && v.get(\"endTime\").map(|n| n.is_i64()).unwrap_or(false)\n}","typeGuard":null,"tryCatchPattern":"// Mirror runtime/cpu_profiler/mod.rs: treat report generation as best-effort —\n// the .cpuprofile file is already flushed; log and continue on InvalidData.\nif let Err(err) = cpuprof::generate_markdown_report(&profile, &md_path, interval) {\n  if err.kind() == std::io::ErrorKind::InvalidData {\n    log::error!(\"profile shape rejected, raw .cpuprofile remains usable: {err}\");\n  } else {\n    log::error!(\"report write failed: {err}\");\n  }\n}","preventionTips":["After upgrading V8/deno_core, run one short `--cpu-profiler` session in CI to catch profile-schema drift early.","Keep the `CpuProfile` structs in sync with the V8 profile format and prefer `#[serde(default)]` for fields V8 may omit.","Always retain the raw `.cpuprofile` as the source of truth; treat .md/.svg as derived artifacts."],"tags":["cpu-profiler","serde","deserialization","v8","runtime"],"backgroundTag":"json-deserialize-schema-mismatch","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}