EpicGames/lore · warning · io::Error

unsupported " ": (expected a whole number of threads)

Error message

unsupported {POOL_THREADS_VAR} "{value}": {error} (expected a whole number of threads)

What it means

max_threads_from_value failed to parse the POOL_THREADS_VAR environment variable as a whole number of threads, so the pool builder rejects the value with ErrorKind::InvalidInput. The library parses the variable eagerly so a bad override fails fast with a clear message instead of falling back silently.

Solutions

  1. Set POOL_THREADS_VAR to a plain non-negative integer, e.g. `export POOL_THREADS_VAR=8`.
  2. Unset the variable entirely to use the library default (min(2 * cores, 16)).
  3. Strip whitespace/units before exporting: use `8` not `"8 threads"`.
  4. If validating user config, parse it first with value.trim().parse::<usize>() and reject early.

Example fix

// before
export POOL_THREADS_VAR="16 threads"
// after
export POOL_THREADS_VAR=16
Defensive patterns

Strategy: validation

Validate before calling

// Rust
fn valid_pool_threads(v: &str) -> Result<usize, String> {
    v.trim().parse::<usize>().map_err(|e| format!("POOL_THREADS_VAR '{v}' invalid: {e} (expected a whole number of threads)"))
}

Try / catch

// Rust
match requested_max_threads() {
    Ok(n) => build_pool(n),
    Err(e) if e.kind() == std::io::ErrorKind::InvalidInput => build_pool(default_threads()), // fallback
    Err(e) => return Err(e.into()),
}

Prevention

When it happens

Trigger: Setting the POOL_THREADS_VAR environment variable to something that is not a valid usize after trimming — e.g. "abc", "3.5", "-2", "4 threads", or a number exceeding usize on this platform — then creating a pool via requested_max_threads.

Common situations: Typo or unit suffix in an env var set in a shell profile, CI config, or Dockerfile; copy-pasting "16 threads" or "16\n" values; locale-formatted numbers like "1_000" or "1,000".

Understand the failure class

Background: "is not a valid" / "Invalid ... value" environment variable errors: how libraries validate env vars and what to do when they reject yours — this error's family across 48 libraries.

Related errors


AI-assisted analysis of EpicGames/lore@074eb0b0d1 (2026-09-13). Data as JSON: /api/errors/742750cbdc15a107. Report an issue: GitHub.

Appendix: source

Thrown at lore-io/src/pool.rs:51

/// stalling async worker threads, while keeping this pool's claim on the process-wide thread budget
/// small enough to leave room for the populations it shares that budget with.
///
/// Doubling the cap buys 4–6% warm and 2–8% cold on Windows/NTFS, and nothing outside ±4% on
/// Linux/ext4. No cap wins every phase, so this is a position on a curve rather than an optimum;
/// `lore-io/BENCHMARKS.md` has the sweeps. What does cost is falling well below the workload's
/// concurrency: 8 threads measured 0.54× against 32 on 16,384 evicted files read at 64-way
/// concurrency, and 4 threads measured 0.65× on macOS/APFS cold reads offering 64 and 128. That
/// ratio is pool size against in-flight requests rather than against core count, so it is what a
/// machine small enough for `2 × cores` to reach those sizes runs into.
pub(crate) fn default_max_threads() -> usize {
    let cores = std::thread::available_parallelism().map_or(2, |count| count.get());
    std::cmp::min(2 * cores, 16)
}

/// Parses a [`POOL_THREADS_VAR`] value. Separate from reading the variable so the accepted range
/// and the error are testable without a process-global environment.
fn max_threads_from_value(value: &str) -> std::io::Result<usize> {
    let invalid = |detail: String| std::io::Error::new(std::io::ErrorKind::InvalidInput, detail);
    let count: usize = value.trim().parse().map_err(|error| {
        invalid(format!(
            "unsupported {POOL_THREADS_VAR} \"{value}\": {error} \
             (expected a whole number of threads)"
        ))
    })?;
    if count == 0 {
        return Err(invalid(format!(
            "{POOL_THREADS_VAR} must be at least 1; a pool of 0 threads runs nothing"
        )));
    }
    if count > MAX_POOL_THREADS {
        return Err(invalid(format!(
            "{POOL_THREADS_VAR} of {count} exceeds the {MAX_POOL_THREADS}-thread ceiling"
        )));
    }
    Ok(count)
}

View on GitHub (pinned to 074eb0b0d1)