neondatabase/neon · warning · ApiError

frequency must be <=1000 Hz

Error message

frequency must be <=1000 Hz

What it means

Returned as HTTP 400 BadRequest by profile_cpu_handler in neon's http-utils when the frequency query parameter of /profile/cpu is >= 1001 Hz. Sampling frequencies above 1000 Hz are rejected to keep profiling overhead and file size sane; the default is 99 Hz and any value up to 1000 is accepted.

Source

Thrown at libs/http-utils/src/endpoint.rs:388

        Pprof,
        Svg,
    }

    // Parameters.
    let format = match get_query_param(&req, "format")?.as_deref() {
        None => Format::Pprof,
        Some("pprof") => Format::Pprof,
        Some("svg") => Format::Svg,
        Some(format) => return Err(ApiError::BadRequest(anyhow!("invalid format {format}"))),
    };
    let seconds = match parse_query_param(&req, "seconds")? {
        None => 5,
        Some(seconds @ 1..=60) => seconds,
        Some(_) => return Err(ApiError::BadRequest(anyhow!("duration must be 1-60 secs"))),
    };
    let frequency_hz = match parse_query_param(&req, "frequency")? {
        None => 99,
        Some(1001..) => return Err(ApiError::BadRequest(anyhow!("frequency must be <=1000 Hz"))),
        Some(frequency) => frequency,
    };
    let force: bool = parse_query_param(&req, "force")?.unwrap_or_default();

    // Take the profile.
    static PROFILE_LOCK: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(()));
    static PROFILE_CANCEL: Lazy<Notify> = Lazy::new(Notify::new);

    let report = {
        // Only allow one profiler at a time. If force is true, cancel a running profile (e.g. a
        // Grafana continuous profile). We use a try_lock() loop when cancelling instead of waiting
        // for a lock(), to avoid races where the notify isn't currently awaited.
        let _lock = loop {
            match PROFILE_LOCK.try_lock() {
                Ok(lock) => break lock,
                Err(_) if force => PROFILE_CANCEL.notify_waiters(),
                Err(_) => {
                    return Err(ApiError::Conflict(

View on GitHub (pinned to 8f60b04da4)

Solutions

  1. Lower frequency to <=1000, e.g. ?frequency=250 or keep the default 99
  2. If you need finer resolution, profile a longer duration instead of a higher frequency
  3. Verify no profile-frequency default in your scrape config exceeds 1000

Example fix

# before
curl 'http://localhost:9898/profile/cpu?frequency=2000'   # 400 frequency must be <=1000 Hz

# after
curl 'http://localhost:9898/profile/cpu?frequency=250'
Defensive patterns

Strategy: validation

Validate before calling

const freq = Number(process.env.PROFILE_FREQUENCY ?? 99);
const safeFreq = Number.isInteger(freq) && freq <= 1000 && freq > 0 ? freq : 99;
await fetch(`/profile/cpu?frequency=${safeFreq}`);

Type guard

function isValidProfileFrequency(f) { const n = Number(f); return Number.isInteger(n) && n >= 1 && n <= 1000; }

Prevention

When it happens

Trigger: GET /profile/cpu?frequency=2000 or ?frequency=1001 — any integer of 1001 or more returns this 400.

Common situations: Copy-pasting a pprof tutorial that recommends high frequencies; trying to get finer-grained stacks on an idle system; Grafana agent config with a high sample rate pointed at the endpoint.

Related errors


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