ErrLookup › Background articles › "must be a positive integer", "cannot be empty", "invalid argument": how invalid-argument errors work across open-source libraries
"must be a positive integer", "cannot be empty", "invalid argument": how invalid-argument errors work across open-source libraries
"must be a positive integer", "cannot be empty", "invalid argument", and "X and Y must differ" are invalid-argument errors: a library or CLI rejected your input before doing any work. This family covers fail-fast guard clauses — zero or negative limits, empty strings, swapped or identical arguments, and out-of-range values — thrown by tools like OpenCLI, LiteLLM, Temporal, Codex, Pulumi, and Hibernate, usually before any network call or filesystem access.
Distilled from 113 documented records across 33 repositories.
Background
Invalid-argument errors are produced at the outermost validation layer of a library or CLI: a guard clause that checks the caller's input against invariants the code cannot proceed without. Unlike network or persistence errors, these fire synchronously and immediately — OpenCLI's limit and timeout parsers throw ArgumentError before opening a browser session, LiteLLM's ModelsManagementClient.get() raises ValueError before any HTTP request, and Temporal's ErrReadTasksNonPositivePageSize rejects a zero page size before touching persistence. The message is typically deterministic and repeats the offending value (OpenCLI embeds it via JSON.stringify, e.g. 'got "abc"'), so the failure is a programming or invocation bug, not an environmental condition.
Each library draws the line slightly differently, and the exact contract matters. Some distinctions are type-level: Codex's memories reader treats max_lines: None as 'read to end of file' and Some(0) as a meaningless request it rejects, and its AllWithinLines mode requires line_count >= 1 because a zero-width co-occurrence window cannot match anything — the schemars schema even declares range(min = 1) so the constraint exists at deserialization too. Others are range checks: OpenCLI's parseStrictIntegerRange enforces a [1, 50] window on list limits, and its pubmed command requires year-from <= year-to because an inverted PDAT date range is impossible. Others are identity or distinctness checks: OpenCLI refuses identical --from/--to IATA codes, Wekan's mergeImportedUserInto throws 'placeholder and target must differ' because merging a user into itself would destructively reassign its own references, and LiteLLM requires exactly one of model_id or model_name since get() filters a downloaded list by a single identifier.
A common subtlety is what counts as 'empty' versus 'absent'. Several libraries deliberately distinguish the two: Mastra rejects an explicit empty-string externalId as a caller bug while allowing undefined (an id is then auto-generated); OpenCLI substitutes a default path for falsy --output values, so the '--output cannot be empty' error only fires when an empty string survives upstream coercion. Whitespace-only strings are another gray area — some guards trim before testing, others do not — so 'cannot be empty' errors can surface from values that look non-empty in the source. Relatedly, some errors are defensive backstops rather than first-line guards: OpenCLI's 'twitter followers user cannot be empty' check is hard to reach because earlier branches throw first, and Appwrite's catch-all on increaseDocumentAttribute forwards the database adapter's own InvalidArgumentException message verbatim, so the text is whatever the underlying adapter (MongoDB, MariaDB, PostgreSQL, MySQL) raised rather than a fixed template.
From the caller's side, these errors share a signature: no side effects have happened yet, the fix is entirely in your input, and retrying unchanged will fail forever. That makes them cheap to debug — read the echoed value, fix the argument — but they also tend to appear in scripts and CI where a shell variable was empty, a unit suffix leaked in ('90s' instead of 90), a locale-formatted number slipped through, or a refactoring left a parameter unset. Whether values are zero-based or 1-based (Temporal's history-queue shard IDs are 1-based), whether 0 means 'unlimited' or is simply invalid (across these records, 0 is always invalid), and whether None/undefined means 'use the default' are all library-specific contracts you must check rather than assume.
Common causes
- Zero or negative value where a positive integer is required. The single most common pattern: limits, page sizes, timeouts, shard IDs, and sequence widths must be >= 1, and 0 is never 'unlimited'. Typically the value came from an uninitialized struct field, a missing flag defaulting to undefined/NaN, or pagination arithmetic that computed zero.
- Empty, whitespace-only, or unset required string. A required argument (a query, handle, output path, endpoint host, user id) arrives as null, undefined, or a string that trims to nothing — usually an omitted flag or an interpolated shell variable that was empty. Some guards trim before testing; others do not, so whitespace-only values can also trigger it.
- Non-integer or malformed numeric input. Floats (2.5), exponent or signed strings ('1e2', '+5'), unit suffixes ('90s'), non-numeric strings ('abc', 'twenty'), and NaN/Infinity all fail strict parsers. OpenCLI's parsers deliberately reject anything that is not a plain decimal integer, even when Number() could coerce it.
- Passing both (or neither) of mutually exclusive arguments. LiteLLM's models.get() requires exactly one of model_id or model_name; Pulumi's plugin rm requires a valid kind as the first positional argument. Wrappers that forward optional kwargs without normalizing absent values to None are a frequent source.
- Identical or self-referential arguments that must differ. OpenCLI rejects from === to IATA codes, Wekan refuses to merge a user into itself, and OpenCLI's pubmed command rejects year-from > year-to. These usually come from programmatically generated pairs, swapped flags, or matching logic that paired an entity with itself.
- Out-of-range values within valid types. The value parses fine but falls outside the allowed window, e.g. OpenCLI list limits capped at [1, 50] or a --digits flag of 0 in golang-migrate. Scripts often assume higher limits mean 'more results' when the cap requires paginating instead.
- Wrong type or sentinel for the language's optional semantics. Codex rejects Some(0) for max_lines because None is the 'no limit' sentinel; Mastra rejects externalId: '' while allowing undefined. Callers who guess the sentinel convention (using 0 or '' to mean 'default/none') hit these guards.
- Malformed identifiers and handles. Values like profile URLs instead of bare handles ('https://x.com/elonmusk'), invalid npm package names, or attribute keys outside the allowed character set fail grammar validation before any API call.
What usually fixes it
- [object Object]
- [object Object]
- [object Object]
- [object Object]
- [object Object]
Go deeper
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Documented occurrences
- twitter followers user cannot be empty (jackwener/OpenCLI)
- Exactly one of model_id or model_name must be provided (BerriAI/litellm)
- all_within_lines.line_count must be a positive integer (openai/codex)
- page size to read history tasks must be positive (temporalio/temporal)
- limit must be a positive integer (jackwener/OpenCLI)
- --output cannot be empty (jackwener/OpenCLI)
- targetCount must be a positive integer, got ${JSON.stringify(targetCount)} (jackwener/OpenCLI)
- max_lines must be a positive integer (openai/codex)
- limit must be a positive integer (jackwener/OpenCLI)
- general_argument_invalid: $e->getMessage() (appwrite/appwrite)
- error-invalid-user: Invalid user provided for erasing team (RocketChat/Rocket.Chat)
- shard ID must be greater than 0 (temporalio/temporal)
- invalid argument (rustfs/rustfs)
- Invalid argument (tursodatabase/turso)
- --from and --to must differ (got ${fromCode}) (jackwener/OpenCLI)
- --${name} must be between ${min} and ${max}, got ${parsed} (jackwener/OpenCLI)
- key cannot be null or empty (hibernate/hibernate-orm)
- twitter followers user must be a valid Twitter/X handle (jackwener/OpenCLI)
- t("providers.oauth_method_required") (different-ai/openwork)
- bad-merge: placeholder and target must differ (wekan/wekan)
…and 93 more across the corpus — use search.
Honest provenance: generated on 2026-09-02 from AI-assisted analysis of the linked records. See how records are made.