can1357/oh-my-pi · error · Error
Unhandled action ${cmd.action}
Error message
Unhandled action ${cmd.action} What it means
`runAction` switches on `cmd.action` and handles info/logs/stop/kill/restart; the `default` branch throws for any other action value reaching the function. It is an internal exhaustiveness guard: the public `PsAction` type also includes "list", which `runPsCommand` handles before calling `runAction`, so this means an unhandled action leaked through dispatch.
Source
Thrown at packages/coding-agent/src/cli/ps-cli.ts:202
case "logs":
await runLogs(cmd, client, name);
return;
case "stop":
case "kill": {
const timeoutMs = cmd.action === "kill" ? KILL_GRACE_MS : Math.round((cmd.flags.timeout ?? 5) * 1000);
const result = await client.request({ op: "stop", name, timeoutMs });
if (result.op !== "stop") throw new Error(`Unexpected broker response ${result.op}`);
printDaemonResult(cmd, cmd.action === "kill" ? "Killed" : "Stopped", result.daemon);
return;
}
case "restart": {
const result = await client.request({ op: "restart", name });
if (result.op !== "restart") throw new Error(`Unexpected broker response ${result.op}`);
printDaemonResult(cmd, "Restarted", result.daemon);
return;
}
default:
throw new Error(`Unhandled action ${cmd.action}`);
}
} catch (error) {
console.error(chalk.red(error instanceof Error ? error.message : String(error)));
process.exitCode = 1;
}
}
function printDaemonResult(cmd: PsCommandArgs, verb: string, daemon: DaemonSnapshot): void {
if (cmd.flags.json) console.log(JSON.stringify(daemon, null, 2));
else console.log(`${verb} ${daemonLabel(daemon)}`);
}
async function runLogs(cmd: PsCommandArgs, client: DaemonBrokerClient, name: string): Promise<void> {
const lines = Math.max(1, Math.min(1_000, Math.floor(cmd.flags.lines ?? 100)));
// Follow mode reads the full 1000-line window on every request so overlap
// trimming sees a stable, sliding tail; the initial print is cut to `lines`.
const first = await client.request({
op: "logs",View on GitHub (pinned to 9690622007)
Solutions
- Check the action string you passed — only info/logs/stop/kill/restart are handled by runAction; route `list` through the list path.
- If you added a new action, add a corresponding `case` in the switch in `runAction` (ps-cli.ts).
- Validate/normalize the action argument at the command-parser boundary before constructing `PsCommandArgs`.
Example fix
// before
switch (cmd.action) {
case "info": /* ... */
case "restart": /* ... */
default:
throw new Error(`Unhandled action ${cmd.action}`);
}
// after
switch (cmd.action) {
case "info": /* ... */
case "restart": /* ... */
case "pause": {
const result = await client.request({ op: "pause", name });
/* ... */
return;
}
default: {
const exhaustive: never = cmd.action;
throw new Error(`Unhandled action ${String(exhaustive)}`);
}
} Defensive patterns
Strategy: validation
Validate before calling
const ACTIONS = ["info", "logs", "stop", "kill", "restart"] as const;
if (!ACTIONS.includes(cmd.action)) {
throw new Error(`Action "${cmd.action}" is not valid here; use ${ACTIONS.join("/")}`);
} Type guard
function isRunnableAction(a: PsAction): a is Exclude<PsAction, "list"> {
return a !== "list";
} Try / catch
try {
await runAction(cmd, name);
} catch (err) {
if (err instanceof Error && err.message.startsWith("Unhandled action")) {
console.error(chalk.red(`${cmd.action} is not supported; run \`omp ps --help\``));
process.exitCode = 1;
return;
}
throw err;
} Prevention
- Validate the action at the argument-parser boundary before building PsCommandArgs.
- Make the switch's default branch use a `never` exhaustiveness check so new union members fail at compile time.
- Keep the public PsAction union and runAction cases in sync when adding actions.
When it happens
Trigger: Calling `runAction` (or `runPsCommand`) with `action` set to "list" or any value outside {info, logs, stop, kill, restart} — e.g. programmatic use of the CLI API, a new action added to `PsAction` without a switch case, or an RPC/plugin invoking the ps command dispatcher directly with an unmapped action.
Common situations: A developer adds a new action to the `PsAction` union and the `PsCommandArgs` parser but forgets to add a `case` in `runAction`; an external caller (script, RPC mode) passes `action: "list"` into `runPsCommand` expecting it to fall through.
Related errors
- --list-details, --exec, and --exec-batch are not supported b
- positional paths cannot be combined with --search-path
- unknown file type: {value}
- invalid size: {value}
- err.to_string() (size parse error)
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/608225877889ab08.
Report an issue: GitHub.