ax-check rule
AXC-C001: Command hangs without a terminal
A probe did not finish within the timeout with no TTY, which usually means it is waiting for input.
ax-check is a checker being prepared for release. This page documents the rule ahead of that release; see all 50 rules.
| Severity | error |
| Kind | Observed. Depends on the program and environment at run time. |
| Mode | ax-check probe-cli |
| Applies to | Command-line programs, run as a subprocess |
| Pattern tags | non-interactive, confirmation |
| Fix in one line | When stdin is not a TTY, never prompt: fail fast with an error that names the flag to pass instead. |
A probe did not finish within the timeout when there was no terminal. This usually means the program is waiting for someone to type an answer. An agent has no keyboard, so the call hangs until something kills it.
What it checks
ax-check probe-cli runs your command with a small set of probes appended (by default --help and --version) with stdin closed (the default, --stdin ignore) and stdout and stderr connected to pipes. Each probe has a timeout of 10 seconds by default (change it with --timeout <ms>). When a probe does not finish in time, ax-check kills the process and reports one finding for that probe.
Why it matters
An agent runs commands through a harness. It cannot answer “Are you sure? [y/N]” or pick an item from a menu. A prompt that waits forever wastes the whole turn and may hold a lock or a connection open. The Command Line Interface Guidelines say: “If stdin is not an interactive terminal, skip prompting and just require those flags/args.” A fast error that names the missing flag is far better than a hang, because the agent can act on it.
How to fix
When stdin is not a TTY, never prompt. If a value is missing, fail fast with a non-zero exit code and an error that names the flag to pass. Offer --yes or --force for confirmations, and make sure the prompt code path is never reached without a TTY.
Example
Before
$ invoicer init </dev/null
Project name:
^C
$ timeout 10 invoicer init </dev/null; echo $?
Project name:
124
The program printed a prompt and waited. Exit status 124 comes from timeout, which killed it after 10 seconds.
After
$ invoicer init </dev/null; echo $?
error: --name is required when input is not a terminal.
Run: invoicer init --name <project-name>
2
A Node fix:
if (!process.stdin.isTTY && !args.name) {
console.error('error: --name is required when input is not a terminal.');
console.error('Run: invoicer init --name <project-name>');
process.exit(2);
}
A Python fix:
if not sys.stdin.isatty() and not args.name:
print("error: --name is required when input is not a terminal.", file=sys.stderr)
print("Run: invoicer init --name <project-name>", file=sys.stderr)
sys.exit(2)
How ax-check detects it
Every probe runs with no TTY and CI=1 set, in a temporary working directory, with HOME and the XDG directories pointing into a temporary directory (unless you pass --real-home). The environment is otherwise inherited, or reduced to PATH with --env-clear. The rule fires once for each probe that hit the timeout: by default help (and its -h fallback), version and no-color; with --probe, also unknown-subcommand, unknown-flag, missing-value, json and the help fallback; with --allow-bare, the bare command. The probe name appears in the finding, so you can see which invocation hung.
To reproduce by hand: timeout 10 invoicer init </dev/null; echo $?. On macOS, timeout comes from GNU coreutils (gtimeout). With --stdin pipe ax-check keeps an open, silent pipe instead of a closed stdin, which is stricter because a program that reads until end of file will then wait.
Known false positives: a command that is meant to run for a long time, such as a server or a file watcher, will time out by design. Point ax-check at a command that finishes, or raise --timeout. Silence the rule with --disable AXC-C001 when the hang is intended.
Safety note: This rule looks at every probe that ran. By default those are --help (and -h if it fails), --version, and --help with NO_COLOR=1. The error, json and help-word probes run only with --probe, and the bare command only with --allow-bare. probe-cli executes your program under your own OS account. The temporary working directory and HOME it uses are not a sandbox: the program can still read and write anything your account can, and use the network. Probe a CLI you have not reviewed only inside a container or a throwaway virtual machine. Run ax-check probe-cli --dry-run -- invoicer first to see the probes that would run, and see the “Probe safety” section of the ax-check README for what is and is not isolated.
Sources
- Guideline: Command Line Interface Guidelines, clig.dev. https://clig.dev/ . States that if stdin is not an interactive terminal, a program should skip prompting and require flags or arguments instead.
Related evidence
Records in the AX evidence register that share a pattern tag with this rule. A shared tag means the record is about the same pattern, not that it tests this rule. Read the evidence class before the number.
- EV-0036: A CLI exits 0 when a destructive command is refused (Independent measurement). Cloudflare's cf CLI documents that in a non-interactive session a destructive command without --force prints 'Aborted.' and exits with status 0, and a public issue reproduces this for workflows delete.
- EV-0014: Allow/ask/never policies blocked less overreach than per-action approval (Preprint). In a study of 113 people without software backgrounds (preprint), user-authored allow/ask/never policies blocked 20.1 percentage points less agent overreach than per-action approval, partly because participants chose 'ask' for 114 of 140 rules and then approved most overreach at runtime.
- EV-0016: Approval records omit the effects a command goes on to trigger (Preprint). A preprint reports that coding-agent approval records name the approved command but omit effects its workflow exercises: across 111 approval and trace pairs, unrecorded residual effects fell from 40 with explicit fields to 17 with command semantics and 13 with decision-time metadata.
- EV-0034: Vendor claim: users approved about 93% of permission prompts (Vendor claim). Anthropic states that its telemetry showed users approved roughly 93% of Claude Code permission prompts, and that an operating-system sandbox reduced permission prompts by 84%.
