denoland/deno · error
Failed to parse profile: {}
Error message
Failed to parse profile: {} What it means
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.
Source
Thrown at runtime/cpu_profiler/cpuprof.rs:67
struct FunctionStats {
function_name: String,
url: String,
line_number: i32,
self_time: i64,
total_time: i64,
self_samples: i32,
total_samples: i32,
}
pub(crate) fn generate_markdown_report(
profile: &serde_json::Value,
filepath: &std::path::Path,
interval_us: i64,
) -> std::io::Result<()> {
let profile: CpuProfile = match serde_json::from_value(profile.clone()) {
Ok(p) => p,
Err(err) => {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("Failed to parse profile: {}", err),
));
}
};
let mut md = String::new();
// Calculate stats
let duration_us = profile.end_time - profile.start_time;
let duration_ms = duration_us as f64 / 1000.0;
let total_samples = profile.samples.len();
let total_functions = profile.nodes.len();
// Build node map
let node_map: HashMap<i32, &ProfileNode> =
profile.nodes.iter().map(|n| (n.id, n)).collect();
View on GitHub (pinned to 9ad36f7a2c)
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.
Example fix
// before (runtime/cpu_profiler/cpuprof.rs)
#[derive(Deserialize)]
pub(crate) struct CpuProfile {
pub nodes: Vec<ProfileNode>,
pub samples: Vec<i32>,
pub time_deltas: Vec<i32>,
// ...
}
// after — tolerate fields V8 makes optional
#[derive(Deserialize)]
pub(crate) struct CpuProfile {
#[serde(default)]
pub nodes: Vec<ProfileNode>,
#[serde(default)]
pub samples: Vec<i32>,
#[serde(default)]
pub time_deltas: Vec<i32>,
// ...
} Defensive patterns
Strategy: try-catch
Validate before calling
// If you call generate_markdown_report on an arbitrary profile value,
// pre-check the shape before deserializing:
fn looks_like_cpuprofile(v: &serde_json::Value) -> bool {
v.get("nodes").map(|n| n.is_array()).unwrap_or(false)
&& v.get("samples").map(|n| n.is_array()).unwrap_or(false)
&& v.get("startTime").map(|n| n.is_i64()).unwrap_or(false)
&& v.get("endTime").map(|n| n.is_i64()).unwrap_or(false)
} Try / catch
// Mirror runtime/cpu_profiler/mod.rs: treat report generation as best-effort —
// the .cpuprofile file is already flushed; log and continue on InvalidData.
if let Err(err) = cpuprof::generate_markdown_report(&profile, &md_path, interval) {
if err.kind() == std::io::ErrorKind::InvalidData {
log::error!("profile shape rejected, raw .cpuprofile remains usable: {err}");
} else {
log::error!("report write failed: {err}");
}
} Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Failed to parse profile: {}
- ReadRawBytes() failed
- cannot use non-object value with an internally tag enum
- buffer must be a TypedArray or a DataView
- source must be a TypedArray or a DataView
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/86d929a4e5838515.
Report an issue: GitHub.