garrytan/gstack · error · Error

Unknown command

Error message

Unknown command: ${c.rawName}

What it means

In the CLI/fallback dispatch path (when `opts.executeCommand` is not provided), each chain subcommand is routed by checking membership in `WRITE_COMMANDS`, `READ_COMMANDS`, then `META_COMMANDS` (lines 680-697). If the canonical name matches none of these sets, the dispatcher throws `Unknown command`. This path runs only in direct-CLI mode; the server path delegates to `handleCommandInternal` instead.

Solutions

  1. Check the subcommand spelling and use the canonical name (run via the server for alias normalization).
  2. If the command is real but only registered server-side, invoke chain through the server (which sets `opts.executeCommand`) instead of the CLI fallback.
  3. Consult the command registry (`WRITE_COMMANDS`/`READ_COMMANDS`/`META_COMMANDS`) to confirm the canonical name.

Example fix

// before
browse chain '[["got","https://x"]]'  // typo
// after
browse chain '[["goto","https://x"]]'
Defensive patterns

Strategy: validation

Validate before calling

import { READ_COMMANDS, WRITE_COMMANDS, META_COMMANDS, canonicalizeCommand } from './commands';
const KNOWN = new Set([...READ_COMMANDS, ...WRITE_COMMANDS, ...META_COMMANDS]);
const unknown = chainCmds.filter(c => !KNOWN.has(canonicalizeCommand(c)));
if (unknown.length) throw new Error(`Unknown chain subcommand(s): ${unknown.join(', ')}`);

Type guard

const isKnownCommand = (name: string): boolean =>
  READ_COMMANDS.has(name) || WRITE_COMMANDS.has(name) || META_COMMANDS.has(name);

Try / catch

try { await browse.chain(payload); }
catch (err) {
  if (/Unknown command/.test(err.message)) {
    // surface the bad subcommand to the caller; do NOT silently retry
  }
}

Prevention

When it happens

Trigger: A chain subcommand whose canonical name is not registered in any of the three command sets — e.g. a typo (`[["got","https://x"]]`), a removed/renamed command, or a command only reachable via server dispatch being run in CLI fallback mode.

Common situations: Running `browse chain` from the CLI (no server) with a command name that exists in the server registry but not in the local sets, or after a rename where old scripts still reference the previous name.

Related errors


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

Appendix: source

Thrown at browse/src/meta-commands.ts:697

            let result: string;
            if (WRITE_COMMANDS.has(name)) {
              if (bm.isWatching()) {
                result = 'BLOCKED: write commands disabled in watch mode';
              } else {
                result = await handleWriteCommand(name, cmdArgs, session, bm);
              }
              lastWasWrite = true;
            } else if (READ_COMMANDS.has(name)) {
              result = await handleReadCommand(name, cmdArgs, session);
              if (PAGE_CONTENT_COMMANDS.has(name)) {
                result = wrapUntrustedContent(result, bm.getCurrentUrl());
              }
              lastWasWrite = false;
            } else if (META_COMMANDS.has(name)) {
              result = await handleMetaCommand(name, cmdArgs, bm, shutdown, tokenInfo, opts);
              lastWasWrite = false;
            } else {
              throw new Error(`Unknown command: ${c.rawName}`);
            }
            results.push(`[${label}] ${result}`);
          } catch (err: any) {
            results.push(`[${label}] ERROR: ${err.message}`);
          }
        }
      }

      // Wait for network to settle after write commands before returning
      if (lastWasWrite) {
        await bm.getPage().waitForLoadState('networkidle', { timeout: 2000 }).catch(() => {});
      }

      return results.join('\n\n');
    }

    // ─── Diff ──────────────────────────────────────────
    case 'diff': {

View on GitHub (pinned to 94993f7401)