std.os.process arguments
std.os.process exposes the arguments typed after the program or script
name. A compiled ./tool alpha beta sees two arguments, alpha and beta.
The program path itself is never counted.
Status: merged to unstable at c70c0099 (PR #41, STD-GAP-064) on
2026-10-11. Not yet part of a tagged release. The examples on this page were
compiled and run on Linux x86_64 with janus build.
Import
Section titled “Import”use std.os.processCompleteness contract
Section titled “Completeness contract”An argument is returned byte-for-byte or not at all.
A shortened argument is not a harmless cosmetic defect. Take a requested
path such as ./././…/config.kdlx that is cut at a buffer boundary: it
becomes ./././…/config.kdl, which may be a different file that exists. The
program then reads, overwrites or deletes a file nobody asked for. Before the
STD-GAP-064 repair the compiled backend did exactly that, silently.
The compiled Linux backend reads /proc/self/cmdline without allocating, so
it has two fixed limits:
| Limit | Value | Checked error |
|---|---|---|
| Whole command line, including the program path and the NUL after each argument | at most 8192 bytes | CliArgError.CommandLineTooLong |
| One returned argument | at most 4095 bytes | CliArgError.ArgumentTooLong |
Both limits are inclusive: a 4095-byte argument is returned whole. The program path counts toward the 8192 bytes, so a binary started from a deep directory has less room for its arguments.
The development interpreter (janus run --jit) holds complete argument
strings and reports only IndexOutOfRange.
Checked API
Section titled “Checked API”Use the checked functions whenever an argument selects data: an input or output path, a key, a host, anything the program acts on.
pub error CliArgError { ReadFailed, CommandLineTooLong, ArgumentTooLong, IndexOutOfRange,}
pub func args_count_checked() -> CliArgError!i64 !{IO, Alloc}pub func arg_at_checked(i: i64) -> CliArgError![*:0]u8 !{IO, Alloc}| Function | Returns | Errors |
|---|---|---|
args_count_checked() | number of arguments after the program path | ReadFailed, CommandLineTooLong |
arg_at_checked(i) | argument i as a NUL-terminated [*:0]u8 | ReadFailed, CommandLineTooLong, ArgumentTooLong, IndexOutOfRange |
| Error | Meaning |
|---|---|
ReadFailed | The command line could not be read, or it was empty. |
CommandLineTooLong | The whole command line exceeds 8192 bytes. No argument is returned, not even a short one. |
ArgumentTooLong | Argument i exceeds 4095 bytes. |
IndexOutOfRange | i is negative or past the last argument. |
Each call reads the command line once, and the error describes that same read. You never pair a status with a different snapshot.
Example
Section titled “Example”use std.os.process
func main() -> i32 do const count = process.args_count_checked() catch |err| do println("command line too long or unreadable") return 2 end if count < 1 do println("usage: tool <input>") return 2 end const path = process.arg_at_checked(0) catch |err| do if err == process.CliArgError.ArgumentTooLong do println("input path is too long") end return 2 end println(path) return 0end| Invocation | Output | Exit |
|---|---|---|
./tool config.kdl | config.kdl | 0 |
./tool | usage: tool <input> | 2 |
./tool <4096-byte path> | input path is too long | 2 |
This program is examples/std_cli_args_checked.jan in the compiler
repository. The command-line arguments tutorial
builds a complete selector step by step.
One buffer, one argument at a time
Section titled “One buffer, one argument at a time”arg_at_checked and arg_at return a pointer into one process-wide buffer.
The pointer is valid until the next arg_at or arg_at_checked call.
Consume or copy an argument before you read the next one. Holding two
argument pointers at once gives you two views of the same, latest argument.
The buffer and the error status are process-global. Read arguments from a single thread, typically at startup.
Legacy API: fails closed
Section titled “Legacy API: fails closed”pub func args_count() -> i64 !{IO, Alloc}pub func arg_at(i: i64) -> [*:0]u8 !{IO, Alloc}The original functions keep their signatures. When an argument or the command line exceeds a limit they cannot say why, so they fail closed:
| Condition | args_count() | arg_at(i) |
|---|---|---|
| Command line over 8192 bytes | 0 | empty string |
Argument i over 4095 bytes | real count | empty string |
i out of range | real count | empty string |
Neither function ever returns a prefix. Earlier versions silently truncated; the STD-GAP-064 repair changed this behavior.
Failing closed is safe but mute. An empty string from arg_at looks the same
as an empty argument passed on purpose, and 0 from args_count looks the
same as no arguments. Use the legacy functions only where that ambiguity is
harmless, such as echoing arguments in diagnostics.
Migration
Section titled “Migration”| Legacy call | Replacement |
|---|---|
process.args_count() | process.args_count_checked() catch … |
process.arg_at(i) | process.arg_at_checked(i) catch … |
Handle at least ArgumentTooLong and CommandLineTooLong with an explicit
refusal and a non-zero exit code.
Capability-taking variants
Section titled “Capability-taking variants”Each function has a _sovereign twin that takes a caps.ProcCap first:
args_count_sovereign, arg_at_sovereign, args_count_checked_sovereign
and arg_at_checked_sovereign. They return exactly what the plain functions
return.
Platform availability
Section titled “Platform availability”The std.os.process facade currently binds the Linux backend
(std/sys/linux/cli_args.zig). Builds for other targets do not get an
argument backend from this module yet. The limits above describe that Linux
backend.
Verification
Section titled “Verification”The native regression suite builds a probe and asserts either the exact argument or a named error for ordinary, empty, absent, at-limit and over-limit arguments, an over-long command line, and both truncation reproductions:
./scripts/zb test-std-cli-args-nativeSources: std/os/process.jan, std/sys/linux/cli_args.zig,
std/os/tests/test_cli_args_native.py.
Related: std.cli parses flags once you hold complete
arguments. Practice the contract in the Monastery mission
04-honest-boundary/01-whole-or-nothing.