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
- Set POOL_THREADS_VAR to a plain non-negative integer, e.g. `export POOL_THREADS_VAR=8`.
- Unset the variable entirely to use the library default (min(2 * cores, 16)).
- Strip whitespace/units before exporting: use `8` not `"8 threads"`.
- 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
- Set POOL_THREADS_VAR to a bare integer like `16`, no units or punctuation
- Unset the variable to accept the default (min(2*cores, 16))
- Validate env-derived config at process startup, fail fast with a clear message
- Test config parsing in CI with a representative env (the library exposes max_threads_from_value for this)
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
- presigned_url_hmac_key is not valid hex
- presigned_url_hmac_key must be at least
- {} {err}
- Failed to start maintenance HTTP server
- Failed to start HTTP server
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)