chatboxai/chatbox · warning · Error

Command is required

Error message

Command is required

What it means

Thrown by executeUserExecCommand() when the command field is missing, empty, or not a string. This is the entry-point validation for the user-exec tool — the function that runs an arbitrary shell command approved by the user. It guards against invoking a shell with an undefined/empty command before any process spawning or logging occurs.

Source

Thrown at src/main/skills/user-exec-runner.ts:66

const activeUserExecCommands = new Map<string, ActiveUserExecCommand>()

function getUserExecKey(sessionId: string | undefined, toolCallId: string | undefined): string | null {
  return toolCallId ? `${sessionId ?? ''}:${toolCallId}` : null
}

export function cancelUserExecCommand(params: Pick<UserExecParams, 'sessionId' | 'toolCallId'>): { killed: boolean } {
  const key = getUserExecKey(params.sessionId, params.toolCallId)
  const active = key ? activeUserExecCommands.get(key) : undefined
  if (!active) return { killed: false }
  active.cancel()
  return { killed: true }
}

export async function executeUserExecCommand(params: UserExecParams): Promise<UserExecResult> {
  const { command, cwd: requestedCwd, timeout, sessionId, toolCallId, approvalSource } = params

  try {
    if (!command || typeof command !== 'string') throw new Error('Command is required')

    const homeDir = os.homedir()
    const cwd = requestedCwd?.trim() || homeDir
    const timeoutMs = timeout || 120_000
    const maxOutputBytes = 1024 * 1024 // 1MB
    const operationId = createOperationId()
    const startedAt = Date.now()

    log.info(
      buildOperationStartLog({
        operationId,
        kind: 'user_exec',
        sessionId,
        toolCallId,
        // Renderer approval metadata is audit-only. Missing values remain visible
        // instead of silently looking like a known authorization path.
        approvalSource: approvalSource ?? 'unknown',
        cwd,

View on GitHub (pinned to 81571269ad)

Solutions

  1. Validate the command field in the tool-call argument schema (zod/string) before reaching executeUserExecCommand.
  2. If the model sends an empty command, treat it as a no-op tool result rather than an error.
  3. Ensure the tool-call deserializer preserves command as a required string field.

Example fix

// before
if (!command || typeof command !== 'string') throw new Error('Command is required')

// after — reject earlier with the tool-call schema so the runner never sees a bad payload
const UserExecArgsSchema = z.object({ command: z.string().min(1), cwd: z.string().optional(), timeout: z.number().optional() })
const parsed = UserExecArgsSchema.parse(params)
// executeUserExecCommand then assumes a valid command
Defensive patterns

Strategy: validation

Validate before calling

import { z } from 'zod'
const UserExecArgsSchema = z.object({
  command: z.string().min(1),
  cwd: z.string().optional(),
  timeout: z.number().int().positive().max(600000).optional(),
  sessionId: z.string().optional(),
  toolCallId: z.string().optional(),
})
const parsed = UserExecArgsSchema.parse(params)
// pass parsed to executeUserExecCommand

Type guard

function isUserExecParams(p: unknown): p is { command: string } {
  return typeof (p as any)?.command === 'string' && ((p as any).command as string).length > 0
}

Try / catch

// executeUserExecCommand returns a UserExecResult envelope; catch for the validation path only.
try {
  const result = await executeUserExecCommand(params)
  if (!result.success) reportError(result.stderr)
} catch (e) {
  if (e instanceof Error && /Command is required/i.test(e.message)) {
    showToast('A command is required')
  } else throw e
}

Prevention

When it happens

Trigger: The LLM tool-call payload omits command, passes an empty string, or passes a non-string (number, object, null). executeUserExecCommand destructures command from params and immediately checks `!command || typeof command !== 'string'`.

Common situations: The model emits a tool call with command: null or command: '' because it reasoned about an empty action; a deserialization bug in the tool-call argument parser drops the field; a test fixture constructs UserExecParams without command. The approval flow (approvalSource) runs regardless, but the command itself must be a non-empty string.

Related errors


AI-assisted analysis of chatboxai/chatbox@81571269ad (2026-08-12). Data as JSON: /api/errors/f07d6949b40e8553. Report an issue: GitHub.