ErrLookupBackground 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

What usually fixes it

Go deeper

Documented occurrences

…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.