JuliusBrussee/caveman · error · Error
requires a value
Error message
${arg} requires a value What it means
Value-taking flags (--days, --provider, --model, --agent, --auth, --out) require exactly one following value. The parser throws if the next token is missing, is another flag (starts with '--'), or contains control characters — it never silently consumes the next flag as a value.
Solutions
- Supply a value immediately after the flag: `--days 30`, `--provider openai`
- Quote values that could be empty or contain odd characters: `--model "gpt-4o"`
- If the value comes from a variable, check it is non-empty before invoking the CLI
Example fix
// before myapp stats --provider // after myapp stats --provider openai
Defensive patterns
Strategy: validation
Validate before calling
function requireValue(argv, flag) {
const i = argv.indexOf(flag);
const v = i !== -1 ? argv[i + 1] : undefined;
if (!v || v.startsWith("--") || /[\u0000-\u001f\u007f]/u.test(v)) {
throw new Error(`${flag} requires a value`);
}
return v;
} Try / catch
try {
const opts = parseStatsOptions(argv);
} catch (e) {
if (String(e.message).endsWith("requires a value")) {
console.error(e.message); process.exitCode = 2;
} else throw e;
} Prevention
- Always place the value immediately after the flag, space-separated
- Quote values; guard interpolated env vars for emptiness before invocation
- Strip trailing flags from scripts so a value-flag is never the last token
When it happens
Trigger: `stats --days` with nothing after it; `stats --provider --json` (next token looks like a flag); a value containing control characters (e.g. from a broken terminal paste or unescaped newline in a script).
Common situations: Truncated command from a shell history paste; forgot the value because it was optional elsewhere; an env var interpolating to empty so the flag becomes last token; values with embedded newlines from YAML/JSON expansion.
Understand the failure class
Background: "no subcommand specified" and "... is required": CLI errors when a required argument is missing — this error's family across 13 libraries.
Related errors
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/7f5e97dc55a8611e.
Report an issue: GitHub.
Appendix: source
Thrown at packages/cli/src/stats-cli.ts:42
export type StatsCLIOptions = {
json: boolean;
plain: boolean;
open: boolean;
out?: string;
filters: string[];
};
export function parseStatsOptions(argv: string[]): StatsCLIOptions {
const result: StatsCLIOptions = { json: false, plain: false, open: false, filters: [] };
const seen = new Set<string>();
const valueFlags = new Set(["--days", "--provider", "--model", "--agent", "--auth", "--out"]);
for (let i = 0; i < argv.length; i++) {
const arg = argv[i]!;
if (seen.has(arg)) throw new Error(`duplicate ${arg}`);
seen.add(arg);
if (valueFlags.has(arg)) {
const value = argv[++i];
if (!value || value.startsWith("--") || /[\u0000-\u001f\u007f]/u.test(value)) throw new Error(`${arg} requires a value`);
if (arg === "--days" && (!/^\d+$/u.test(value) || Number(value) < 1 || Number(value) > 3660)) {
throw new Error("--days must be an integer from 1 to 3660");
}
if (arg === "--out") result.out = value;
else result.filters.push(arg, value);
} else if (arg === "--json") result.json = true;
else if (arg === "--plain" || arg === "--no-open") result.plain = true;
else if (arg === "--open") result.open = true;
else if (arg === "--all-time") result.filters.push("--days", "0");
else throw new Error(`unknown option ${arg}`);
}
if (seen.has("--days") && seen.has("--all-time")) throw new Error("choose --days or --all-time");
if (result.plain && result.open) throw new Error("choose --plain or --open");
if (result.json && result.open) throw new Error("--json cannot open a browser");
return result;
}
type StatsMetrics = {View on GitHub (pinned to 3ee70a1026)