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

  1. Rebuild/redeploy the service with jemalloc heap profiling enabled (the jemalloc_pprof PROF_CTL must be initialized at startup)
  2. Use a build where the service links jemalloc with opt.prof:true so PROF_CTL is Some
  3. 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

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


AI-assisted analysis of neondatabase/neon@8f60b04da4 (2026-08-16). Data as JSON: /api/errors/597ac11a1cb31a1b. Report an issue: GitHub.