vectordotdev/vector · error

max_decompressed_size_bytes already set

Error message

max_decompressed_size_bytes already set

What it means

vector-common keeps the global decompression-size cap (used by all sources/decoders to stop decompression bombs) in a std::sync::OnceLock. `set_max_decompressed_size_bytes` initializes that cap plus two derived limits and is documented to be called exactly once, before any sources start. Calling it a second time makes OnceLock::set return Err, and this expect panics with 'max_decompressed_size_bytes already set'.

Source

Thrown at lib/vector-common/src/decompression.rs:79

        .saturating_div(1000)
        .saturating_add(11) as usize
}

const DEFAULT_MAX_ZLIB_COMPRESSED_FRAME_SIZE_BYTES: usize =
    zlib_compressed_frame_limit(DEFAULT_MAX_DECOMPRESSED_SIZE_BYTES);

const DEFAULT_MAX_ZSTD_WINDOW_LOG: Option<u32> =
    zstd_window_log_max(DEFAULT_MAX_DECOMPRESSED_SIZE_BYTES);

/// Override the global decompressed payload size cap. Must be called before any sources start.
///
/// # Panics
///
/// Panics if called more than once, as the global cap may only be initialized a single time.
pub fn set_max_decompressed_size_bytes(size: usize) {
    MAX_DECOMPRESSED_SIZE_BYTES
        .set(size)
        .expect("max_decompressed_size_bytes already set");
    MAX_ZLIB_COMPRESSED_FRAME_SIZE_BYTES
        .set(zlib_compressed_frame_limit(size))
        .expect("max_zlib_compressed_frame_size_bytes already set");
    MAX_ZSTD_WINDOW_LOG
        .set(zstd_window_log_max(size))
        .expect("max_zstd_window_log already set");
}

/// Returns the currently configured decompressed payload size cap.
pub fn max_decompressed_size_bytes() -> usize {
    *MAX_DECOMPRESSED_SIZE_BYTES
        .get()
        .unwrap_or(&DEFAULT_MAX_DECOMPRESSED_SIZE_BYTES)
}

/// Returns the maximum compressed frame wire size we are willing to buffer, derived from the
/// decompressed cap plus zlib's worst-case expansion. See `zlib_compressed_frame_limit`.
pub fn max_zlib_compressed_frame_size_bytes() -> usize {

View on GitHub (pinned to 3708c39b12)

Solutions

  1. Call set_max_decompressed_size_bytes exactly once, at process start, before spawning any sources
  2. Wrap the initialization in std::sync::Once or a OnceLock<()> in your own setup helper so repeat calls become no-ops
  3. In test suites, move the call to a shared once-per-process helper (one #[ctor] or the first line of a common fixture) instead of per-test setup

Example fix

// before: panics when setup() runs twice
fn setup(size: usize) {
    vector_common::decompression::set_max_decompressed_size_bytes(size);
}

// after: idempotent
static INIT: std::sync::Once = std::sync::Once::new();
fn setup(size: usize) {
    INIT.call_once(|| {
        vector_common::decompression::set_max_decompressed_size_bytes(size);
    });
}
Defensive patterns

Strategy: validation

Validate before calling

static INIT: std::sync::Once = std::sync::Once::new();

fn init_decompression_cap(size: usize) {
    INIT.call_once(|| {
        vector_common::decompression::set_max_decompressed_size_bytes(size);
    });
}

Try / catch

let result = std::panic::catch_unwind(|| {
    set_max_decompressed_size_bytes(size);
});
if result.is_err() {
    // already initialized earlier in this process; safe to ignore in test harnesses
}

Prevention

When it happens

Trigger: Calling vector_common::decompression::set_max_decompressed_size_bytes twice in one process — e.g. a test harness that initializes per test without a once-guard, or two embedded components (CLI flag handler plus app init) both configuring the cap.

Common situations: Embedding Vector crates as a library; integration/cargo test suites where every test calls setup; refactors that moved the init call so it now runs on both the CLI path and the library path.

Related errors


AI-assisted analysis of vectordotdev/vector@3708c39b12 (2026-08-20). Data as JSON: /api/errors/8e0dc7ad90d67aaf. Report an issue: GitHub.