garrytan/gstack · warning · Error

--out requires a file path

Error message

--out requires a file path

What it means

Thrown by parseOutArgs when `--out` (space form) is followed by nothing (end of args) or by another flag (a token starting with --). The parser treats the next token as the path only if it exists and does not look like a flag.

Source

Thrown at browse/src/read-commands.ts:74

/**
 * Parse `--out <path>` / `--out=<path>` and `--raw` / `--raw=true|false` out of an
 * arg list, returning the flags plus the remaining positional args (`rest`).
 *
 * Single source of truth shared by the js/eval handlers and the write-capability
 * gate in server.ts, so the two never disagree on what counts as an `--out`
 * invocation. Throws on malformed usage (repeated `--out`, missing value, bad
 * `--raw` value) so the user gets a clear error instead of a silent misparse.
 */
export function parseOutArgs(args: string[]): OutArgs {
  let outPath: string | undefined;
  let raw = false;
  const rest: string[] = [];
  for (let i = 0; i < args.length; i++) {
    const a = args[i];
    if (a === '--out') {
      if (outPath !== undefined) throw new Error('--out specified more than once');
      const val = args[i + 1];
      if (val === undefined || val.startsWith('--')) throw new Error('--out requires a file path');
      outPath = val;
      i++;
    } else if (a.startsWith('--out=')) {
      if (outPath !== undefined) throw new Error('--out specified more than once');
      const val = a.slice('--out='.length);
      if (val === '') throw new Error('--out requires a file path');
      outPath = val;
    } else if (a === '--raw') {
      raw = true;
    } else if (a.startsWith('--raw=')) {
      const v = a.slice('--raw='.length).toLowerCase();
      if (v !== 'true' && v !== 'false') throw new Error('--raw must be true or false');
      raw = v === 'true';
    } else {
      rest.push(a);
    }
  }
  return { outPath, raw, rest };

View on GitHub (pinned to 94993f7401)

Solutions

  1. Provide a path immediately after --out
  2. Use the --out=path form to avoid ambiguity with following flags
  3. Guard empty shell variables: `${OUT:?missing}` or quote explicitly
  4. Check arg ordering — put --out last or use the equals form

Example fix

# before
browse js 'document.title' --out            # throws — no path
browse js 'document.title' --out --raw      # throws — next is a flag

# after
browse js 'document.title' --out ./out.json
browse js 'document.title' --out=./out.json --raw
Defensive patterns

Strategy: validation

Validate before calling

function outHasValue(args: string[]): boolean {
  for (let i = 0; i < args.length; i++) {
    if (args[i] === '--out') {
      const next = args[i + 1];
      if (next === undefined || next.startsWith('--')) return false;
    }
  }
  return true;
}

if (!outHasValue(args)) {
  throw new Error('--out must be followed by a file path');
}

Type guard

function outValueIsPresent(args: string[]): boolean {
  for (let i = 0; i < args.length; i++) {
    if (args[i] === '--out') {
      const v = args[i + 1];
      if (!v || v.startsWith('--')) return false;
    }
  }
  return true;
}

Try / catch

try {
  parseOutArgs(args);
} catch (e: any) {
  if (/--out requires a file path/.test(e.message)) {
    // fall back to a default path
    parseOutArgs([...args, './out.json']);
  } else throw e;
}

Prevention

When it happens

Trigger: `browse js <expr> --out` at the end of the arg list, or `browse js <expr> --out --raw` where the token after --out is itself a flag.

Common situations: Forgetting the path entirely; misordered flags; shell word-splitting that turned an empty variable into nothing; copy-paste that dropped the path.

Related errors


AI-assisted analysis of garrytan/gstack@94993f7401 (2026-08-12). Data as JSON: /api/errors/61f0d31deec3f2d7. Report an issue: GitHub.