garrytan/gstack · warning · Error

load-html: --from-file requires a path

Error message

load-html: --from-file requires a path

What it means

Argument guard at write-commands.ts:192. The --from-file flag consumes the next token via args[++i]; if that token is undefined (--from-file is the last arg) or empty, payloadPath is falsy and the command refuses to proceed rather than reading an unintended path.

Source

Thrown at browse/src/write-commands.ts:192

      await page.reload({ waitUntil: 'domcontentloaded', timeout: 15000 });
      return `Reloaded ${page.url()}`;
    }

    case 'load-html': {
      if (inFrame) throw new Error('Cannot use load-html inside a frame. Run \'frame main\' first.');

      // --from-file <path.json>: read inline HTML from a JSON payload. Used by
      // make-pdf to dodge Windows argv size limits on large rendered HTML.
      // The JSON shape is { html: string, waitUntil?: "load"|"domcontentloaded"|"networkidle" }.
      // The safe-dirs + magic-byte + size-cap checks below still apply to the
      // INLINE HTML content, not to the payload file path itself.
      let fromFilePayload: { html: string; waitUntil?: SetContentWaitUntil } | null = null;
      let filePath: string | undefined;
      let waitUntil: SetContentWaitUntil = 'domcontentloaded';
      for (let i = 0; i < args.length; i++) {
        if (args[i] === '--from-file') {
          const payloadPath = args[++i];
          if (!payloadPath) throw new Error('load-html: --from-file requires a path');
          // Parity with the sibling `load-html <file>` path below (line 249):
          // that branch runs every `file://` target through validateReadPath
          // so the safe-dirs policy can't be side-stepped. Same policy must
          // apply here — otherwise --from-file becomes a read-anywhere escape
          // hatch for any caller that can pick the payload path (e.g., an
          // MCP caller issuing load-html with an attacker-influenced path).
          try {
            validateReadPath(path.resolve(payloadPath));
          } catch {
            throw new Error(
              `load-html: --from-file ${payloadPath} must be under ${SAFE_DIRECTORIES.join(' or ')} (security policy). Copy the payload into the project tree or /tmp first.`
            );
          }
          const raw = fs.readFileSync(payloadPath, 'utf8');
          let json: any;
          try { json = JSON.parse(raw); }
          catch (e: any) { throw new Error(`load-html: --from-file JSON parse failed: ${e.message}`); }
          if (typeof json.html !== 'string') {

View on GitHub (pinned to 94993f7401)

Solutions

  1. Supply the payload path immediately after the flag: load-html --from-file ./payload.json
  2. Validate the args array length in automation before invoking
  3. Ensure the path token is non-empty and present

Example fix

// before
await handleWriteCommand('load-html', ['--from-file'], session, bm)
// after
await handleWriteCommand('load-html', ['--from-file', './payload.json'], session, bm)
Defensive patterns

Strategy: validation

Validate before calling

function requireFromFilePath(args: string[], i: number): string {
  const p = args[i + 1]
  if (!p) throw new Error('--from-file requires a path')
  return p
}

Type guard

function hasFromFileTarget(args: string[]): boolean {
  const i = args.indexOf('--from-file')
  return i >= 0 && typeof args[i + 1] === 'string' && args[i + 1].length > 0
}

Prevention

When it happens

Trigger: load-html --from-file with no following token (last argument), or followed by an empty string; e.g. 'load-html --from-file' alone.

Common situations: Truncated command, a templating bug that drops the path, or quoting that yields an empty value.

Related errors


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