neondatabase/neon · error · ApiError
heap profiling not enabled
Error message
heap profiling not enabled
What it means
Returned as HTTP 500 InternalServerError by profile_heap_handler in neon's http-utils when jemalloc_pprof::PROF_CTL is None, i.e. the binary was built without jemalloc heap-profiling support compiled in (the jemalloc_pprof feature that initializes the global PROF_CTL static never ran). The endpoint exists on every http-utils server, but the capability is compile-time optional, so requesting /profile/heap on a build without it fails at the handle-lookup step.
Source
Thrown at libs/http-utils/src/endpoint.rs:483
enum Format {
Jemalloc,
Pprof,
Svg,
}
// Parameters.
let format = match get_query_param(&req, "format")?.as_deref() {
None => Format::Pprof,
Some("jemalloc") => Format::Jemalloc,
Some("pprof") => Format::Pprof,
Some("svg") => Format::Svg,
Some(format) => return Err(ApiError::BadRequest(anyhow!("invalid format {format}"))),
};
// Obtain profiler handle.
let mut prof_ctl = jemalloc_pprof::PROF_CTL
.as_ref()
.ok_or(ApiError::InternalServerError(anyhow!(
"heap profiling not enabled"
)))?
.lock()
.await;
if !prof_ctl.activated() {
return Err(ApiError::InternalServerError(anyhow!(
"heap profiling not enabled"
)));
}
// Take and return the profile.
match format {
Format::Jemalloc => {
// NB: file is an open handle to a tempfile that's already deleted.
let file = tokio::task::spawn_blocking(move || prof_ctl.dump())
.await
.map_err(|join_err| ApiError::InternalServerError(join_err.into()))?
.map_err(ApiError::InternalServerError)?;View on GitHub (pinned to 8f60b04da4)
Solutions
- Rebuild/redeploy the service with jemalloc heap profiling enabled (the jemalloc_pprof PROF_CTL must be initialized at startup)
- Use a build where the service links jemalloc with opt.prof:true so PROF_CTL is Some
- If you cannot rebuild, fall back to OS-level tooling (e.g. pmap, jemalloc's MALLCTL via a profiler-enabled build) or CPU profiles via /profile/cpu
Example fix
# before neon_pageserver ... # built without jemalloc profiling; /profile/heap -> 500 # after # build with jemalloc profiling feature enabled, then: curl 'http://localhost:9898/profile/heap?format=pprof'
Defensive patterns
Strategy: type-guard
Validate before calling
# Probe capability before wiring continuous heap profiling: if ! curl -sf http://localhost:9898/profile/heap -o /dev/null; then echo "binary lacks jemalloc profiling support; rebuild with the jemalloc_pprof feature" fi
Type guard
// In Rust, gate the endpoint registration on the capability:
if jemalloc_pprof::PROF_CTL.as_ref().is_some() {
router.get("/profile/heap", profile_heap_handler);
} Prevention
- Build binaries for profiling use with jemalloc profiling enabled
- Capability-check /profile/heap once at deploy time rather than at each scrape
- Keep separate debug/profiling and production build profiles
When it happens
Trigger: GET or POST /profile/heap on a neon service binary compiled without jemalloc profiling enabled; PROF_CTL.as_ref() evaluates to None and the handler immediately returns this 500 before inspecting any parameters.
Common situations: Debug builds or production binaries built with default features; deploying a binary compiled on a toolchain/platform where jemalloc (and its prof feature) is not enabled; expecting heap profiles because the docs describe the endpoint without noting the build flag.
Related errors
- invalid format {format}
- duration must be 1-60 secs
- frequency must be <=1000 Hz
- Cannot manage failpoints because neon was compiled without f
- Failed to configure failpoints: {err_msg}
AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16).
Data as JSON: /api/errors/597ac11a1cb31a1b.
Report an issue: GitHub.