The command succeeded. For a sweep, at least one name is available.
Output and exit codes
What reserve prints, where it prints it, and the codes a script can branch on.
Exit codes
These are a contract and don't change meaning within a major version.
0exit code1exit codeThe sweep ran and nothing is available. This covers both "every name is taken"
and "some lookups couldn't be answered": read the unknown count from the
tally, or from --json, to tell the two apart.
2exit codeThe arguments were wrong.
4exit codeThe network couldn't be reached, including a stall where every registry
refused or went silent partway through. reserve serve also exits here when
it can't bind the address it was given, or when the server it started stops
answering.
5exit codeA file couldn't be written. This also covers the tool failing to start, such as a terminal it can't take over.
130exit codeThe run was interrupted (Ctrl-C, or the equivalent signal on your platform).
101exit codeThe tool panicked. This is a bug, and a backtrace follows when
RUST_BACKTRACE=1 is set.
$ reserve your-domain-name --tld com,net,io,dev,com.bd,net.bd
...
4 available 2 taken 0 unknown (6 checked)
$ echo $?
0
$ reserve example.com --tld com
DOMAIN STATUS SOURCE TIME NOTE
- example.com TAKEN registry 1104ms
0 available 1 taken 0 unknown (1 checked)
$ echo $?
1# a typical CI script branching on the exit code
if reserve "$1" --tld com,net --available-only --quiet; then
echo "still free"
else
echo "someone already registered it"
fiStable failure identifiers
Every failure also carries a stable identifier, printed as the last line of the
error block on stderr: code: <identifier>. A script can match on this
instead of parsing the error message.
error: <message>
caused by: <cause>
try: <remedy>
code: <identifier>Identifiers that mean the arguments were wrong (exit 2):
file.unreadable: the named file couldn't be readcatalog.malformed: the catalog data couldn't be parsedcatalog.version: a data file is stamped with a schema version this build doesn't read, so it's refused rather than loaded and misreadcatalog.empty_selection: the filters given leave no extension to checkcatalog.restricted_only: every match is one the public can't register undergroup.unknown: the named group isn't a known groupextension.invalid: the given value isn't a usable domain extensionfilter.invalid: the given value isn't usable for that settingname.invalid: the given name isn't a usable domain namename.list_empty: no name was given to check
Identifiers that mean the network failed (exit 4):
bootstrap.unavailable: the registry server list couldn't be fetched and no cached copy is usablenetwork.unreachable: no lookup reached a registryregistry.refused: every registry refused the question, which usually means this address is being throttledregistry.silent: every registry stopped answering part way throughserver.bind_failed:reserve servecouldn't bind the address it was givenserver.stopped: the bind succeeded and the server ran, then stopped answering
Identifiers that mean a file couldn't be written (exit 5):
output.unwritable: output couldn't be written to the named target
A stall report (network.unreachable, registry.refused, or registry.silent)
looks like this on stderr:
network: no lookup reached a registry, so this machine may have no working connection
code: network.unreachableThe diagnosis comes from the lookups the run already made, not a separate connectivity check: a pre-flight probe would race the real request and report success behind a captive portal, so there is none. A single answered lookup means the network is never blamed, whatever else failed.
stdout vs. stderr
Results go to stdout. Progress, warnings, prompts, and errors go to stderr, so a pipe receives only data. When stdout isn't a terminal, decoration is dropped and the output stays parseable. A closed pipe ends the run quietly rather than panicking.
On a terminal, a run opens with its start time and closes with its duration, both on stderr:
started 2026-08-20T00:34:22+06:00
+ your-domain-name.com.bd AVAILABLE text 803ms
finished 1 checked in 1.29sThe timestamp carries its offset, so a line pasted into an issue says which clock it came from.
A long sweep draws a one-line progress indicator on stderr, naming the domain in hand alongside the count:
checking 14 of 22 your-domain-name.shop 1sIt never touches stdout, and (along with the start/finish lines above) it stays
away entirely when stderr isn't a terminal, in continuous integration, under
--json or --no-input, or whenever colour is suppressed. -q/--quiet drops
it too; --progress always or --progress never overrides the automatic
decision either way.
Colour
Colour follows these rules, first match wins:
- An explicit
--color alwaysor--color neverflag. NO_COLORset to a non-empty value, orCLICOLOR=0: colour off.FORCE_COLOR(non-empty, not0) orCLICOLOR_FORCE(anything but0): colour on, even through a pipe.- A dumb terminal (
TERMunset, empty, ordumb): colour off. - Otherwise, whether that stream is a terminal, decided separately for stdout and stderr, so a piped stdout can stay plain while an attached stderr keeps colour.
Log output takes the stderr half of that decision, so --color never,
NO_COLOR, or a piped stderr strips the colour from -v logs the same way it
strips it from everything else.
Colours come from the sixteen named terminal colours, so retheming your terminal rethemes the tool. Meaning is never carried by colour alone. A symbol or a word always carries it too.