Hmbown/CodeWhale · error · Error

Unknown or empty option. Use --help.

Error message

Unknown or empty option. Use --help.

What it means

scripts/pet.mjs validates every CLI argument: only bare flags --demo, --watch, --resume are allowed, and every other option must be --name=value with a non-empty value drawn from the known set (input, output, trace, format, runtime, thread, segment-buckets). Anything else — an unknown option, a bare --name without =value, or an empty value — throws this error and the script exits.

Solutions

  1. Run node scripts/pet.mjs --help and use only the documented options.
  2. Rewrite space-separated options to = form: --input trace.jsonl becomes --input=trace.jsonl.
  3. Fix typos so the option name matches the regex ^--(input|output|trace|format|runtime|thread|segment-buckets)=.+
  4. Remove stray positional arguments or unsupported flags from the invoking script.

Example fix

// before
node scripts/pet.mjs --input trace.jsonl --verbose
// after
node scripts/pet.mjs --input=trace.jsonl --output=pet.jsonl
Defensive patterns

Strategy: validation

Validate before calling

const KNOWN_FLAGS = ['--demo', '--watch', '--resume'];
const KNOWN_OPTS = /^(--)(input|output|trace|format|runtime|thread|segment-buckets)=.+$/;
args.forEach(a => { if (!KNOWN_FLAGS.includes(a) && !KNOWN_OPTS.test(a)) console.error(`bad arg: ${a}`); });

Try / catch

try {
  await main(process.argv.slice(2));
} catch (err) {
  if (err.message.startsWith('Unknown or empty option')) {
    console.error(err.message); process.exit(2);
  }
  throw err;
}

Prevention

When it happens

Trigger: Passing an unrecognized flag (e.g. --verbose), passing an option without a value (--input with nothing after it), a misspelled option (--outpt=...), or a positional argument that doesn't start with --.

Common situations: Typos in long option names; copying examples that use --input value (space-separated) instead of --input=value; shell scripts passing through extra flags; old documentation referencing renamed options.

Understand the failure class

Background: "Unknown argument", "Invalid value", and "must be one of": invalid CLI argument errors explained — this error's family across 35 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/a813bd0ba3c55d7f. Report an issue: GitHub.

Appendix: source

Thrown at pet/scripts/pet.mjs:20

/** Read-only adapter: existing Whalesong ingestion is the only event parser. */
import { readFile, stat, open } from 'node:fs/promises';
import { watch } from 'node:fs';
import { basename, dirname } from 'node:path';
import { importTrace } from '../dist/core/ingest.js';
import { compilePetTelemetry, encodePetJSONL, encodePetTSV } from '../dist/core/pet-telemetry.js';
import { petDemoEvents } from '../dist/core/pet-demo.js';
import { followRuntime } from './lib/pet-runtime.mjs';
import { createPetRecorder } from './lib/pet-recorder.mjs';

const args = process.argv.slice(2);
const option = name => args.find(a => a.startsWith(`--${name}=`))?.slice(name.length + 3);
if (args.includes('--help')) {
  console.log('node scripts/pet.mjs --input=trace.jsonl --output=pet.jsonl [--trace=ID] [--format=jsonl|tsv] [--watch]\nnode scripts/pet.mjs --runtime=http://127.0.0.1:7878 --thread=ID --output=pet.jsonl [--segment-buckets=216000] [--resume]\nUse --demo instead of --input for synthetic telemetry. Output must not already exist unless --resume is used for live recording. A resumed recorder preserves the previous segment and starts unknown at the same path.\nLive recording rotates at 216000 buckets or 64 MiB into OUTPUT.segment-NNNNNN.jsonl and continues at the same live path. All archives are retained.\nRuntime reads only the existing local event journal. Optional authentication comes from CODEWHALE_RUNTIME_TOKEN; never put a token in the URL. No agent or provider is started.');
  process.exit(0);
}
let output, recorder, monitor, timer, runtime;
try {
  for (const a of args) if (!['--demo', '--watch', '--resume'].includes(a) && !/^--(input|output|trace|format|runtime|thread|segment-buckets)=.+/.test(a)) throw new Error('Unknown or empty option. Use --help.');
  const input = option('input'), runtimeURL = option('runtime'), path = option('output'), format = option('format') ?? 'jsonl', live = args.includes('--watch') || !!runtimeURL;
  if (!path || [!!input, args.includes('--demo'), !!runtimeURL].filter(Boolean).length !== 1
    || !['jsonl', 'tsv'].includes(format) || live && format !== 'jsonl' || args.includes('--watch') && !input
    || !!runtimeURL !== !!option('thread') || option('trace') && !input || option('segment-buckets') && !live || args.includes('--resume') && !live)
    throw new Error('Choose one input source, an unused --output path (or --resume), and JSONL for live recording. Runtime requires --thread.');
  const load = async () => {
    if (!input) return { events: petDemoEvents(), duration: 80_000 };
    if ((await stat(input)).size > 64 * 1024 * 1024) throw new Error('Input exceeds 64 MiB.');
    const traces = importTrace(await readFile(input, 'utf8'), input, { privacy: 'metadata' });
    const trace = option('trace') ? traces.find(t => t.id === option('trace')) : traces.length === 1 ? traces[0] : undefined;
    if (!trace) throw new Error('Select an existing --trace ID when input contains multiple traces.');
    return trace;
  };
  let trace = runtimeURL ? undefined : await load(), buckets = compilePetTelemetry(trace?.events ?? [], trace?.duration ?? 0);
  if (live) recorder = await createPetRecorder(path, { resume: args.includes('--resume'), maxBuckets: option('segment-buckets') === undefined ? 216_000 : Number(option('segment-buckets')), report: text => console.error(text) });
  else output = await open(path, 'wx', 0o600);
  if (runtimeURL) runtime = await followRuntime({ baseUrl: runtimeURL, threadId: option('thread'),
    token: process.env.CODEWHALE_RUNTIME_TOKEN, report: text => console.error(text) });

View on GitHub (pinned to 433685b202)