ErrLookupBackground articles › "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass

"Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass

Invalid argument value errors, the family behind messages like 'must be a positive integer', 'Invalid value for', 'Unsupported network', and 'Version doesn't exist!', fire when an argument you pass falls outside what a library accepts: not in its closed set of names, not a positive finite number, not the right shape. Developers meet them as ArgumentError, ValueError, or IllegalArgumentException when unvalidated config, env vars, or request parameters reach an API, when a token is misspelled or wrongly cased, or when a computed value degenerates to zero, negative, NaN, or Infinity. The throw is deliberate and early, before side effects, and the fix almost always belongs in the calling code, not the library.

Distilled from 96 documented records across 35 repositories.

Background

Every error in this family comes from an explicit validation at a library's public boundary, not from an operation that went wrong halfway through. The records consistently show the check running before any side effect: the airi screen-capture handler validates its timeout before the setSource mutex is acquired, the Chroma JavaScript client rejects a bad Knn limit before any network request, turso's retryFetch validates its attempts count when the wrapper is constructed rather than per request, and tailscale's firewall helper returns 'unsupported network' before touching iptables. The guards exist because proceeding would produce silent nonsense instead of a clean error: kaminari rejects negative padding because it would become a negative SQL OFFSET, matplotlib's Sankey refuses a radius greater than its gap because the paths would overlap, the integer tick locator rejects a non-positive step because every later operation divides by it, and tailscale's distsign refuses to sign a zero-length package because the signature could never be reproduced. From the caller's side the experience is uniform: an immediate, synchronous throw with no partial state to clean up, and the offending value usually visible in the message.

What varies is the kind of constraint being enforced. The largest group is closed vocabularies: Faraday's request_timeout accepts exactly :read, :write, or :open; Guard's pause accepts :paused, :unpaused, or :toggle; pnpm's runtime set is node, deno, bun; webmock accepts four notation symbols; tailscale accepts only the strings udp4 and udp6; zed enumerates prediction providers; yt-dlp validates youtube:lang against a hard-coded case-sensitive list; carrierwave looks up version names declared in the uploader. A second group enforces numeric domains: strictly positive (matplotlib's linthresh and tick step, Intervention's color limit, hadoop's leastPowerOfTwo), positive and finite (airi's timeout, deno's escapeCodeTimeout), or an integer greater than zero (Chroma's Knn limit and Rrf k, turso's retry attempts). A third group checks shape: matplotlib's button layout must be None, 'vertical', 'horizontal', or a tuple of two plain ints, and relative inset sizes require a 4-tuple or Bbox anchor. A fourth group checks arguments against each other: matplotlib's radius against gap, hadoop's maxSize against step, and rustfs' requirement that the version slice length equal the caller-recorded num_versions.

The error class and message quality are library-specific. Ruby libraries raise ArgumentError (carrierwave, kaminari, faraday, webmock, guard, dotenv); Python and JavaScript raise ValueError or TypeError (matplotlib widgets, Chroma's rank factories); Java throws IllegalArgumentException or HadoopIllegalArgumentException (hibernate, hadoop); PHP's Intervention throws InvalidArgumentException; Rust surfaces either a CLI parse failure (zed) or an io::Error with ErrorKind::InvalidInput (rustfs). Some messages interpolate the offending value, such as dotenv's inspect output, deno's 'Received ...', rustfs' expected/got pair, or tailscale's 'got %d'; others enumerate the accepted set, like zed's provider list, yt-dlp's supported language codes, pnpm's runtime names, or webmock's notation list; the most useful do both.

Coercion policy differs sharply between libraries and is worth checking for the exact API in hand. Kaminari coerces with to_i before checking sign, so the string '-5' fails exactly like -5; dotenv deliberately does no coercion, so the string 'true' is rejected as an overwrite flag; Chroma requires a real integer, so the string '60' fails Number.isInteger; yt-dlp matches language codes exactly, case included. Even the messages can mislead: the Deno polyfill reports the default 500 rather than the value actually passed, zed's provider error omits the accepted 'baseten' token, and turso's 'finite integer' wording lets 2.5 through. Where libraries disagree like this, the details are library-specific; the shared shape of the failure, an argument rejected at the boundary before anything happens, is what makes this one family.

Common causes

What usually fixes it

Go deeper

Documented occurrences

…and 76 more across the corpus — use search.

Honest provenance: generated on 2026-08-23 from AI-assisted analysis of the linked records. See how records are made.