ComposioHQ/composio · error · Error

Local command failed for ${context.finalSlug}: ${invocation.

Error message

Local command failed for ${context.finalSlug}: ${invocation.command} ${(invocation.args ?? []).join(' ')}
exitCode=${exitCode ?? 'null'} signal=${signal ?? 'null'}
stderr: ${stderr.trim()}

What it means

The spawned local command exited with a non-zero exit code (or was killed by a signal). The error aggregates the full command line, exitCode, signal, and trimmed stderr so the underlying CLI failure is visible. This is a wrapper for any downstream executable failure, not an SDK bug per se.

Source

Thrown at ts/packages/cli-local-tools/src/runtime.ts:142

    child.stdin.end(invocation.stdin);
  } else {
    child.stdin.end();
  }

  let timeout: NodeJS.Timeout | undefined;
  if (invocation.timeoutMs && invocation.timeoutMs > 0) {
    timeout = setTimeout(() => child.kill('SIGTERM'), invocation.timeoutMs);
  }

  const exitPromise = once(child, 'exit') as Promise<[number | null, NodeJS.Signals | null]>;
  const errorPromise = once(child, 'error').then(([error]) => {
    throw error;
  }) as Promise<[number | null, NodeJS.Signals | null]>;
  const [exitCode, signal] = await Promise.race([exitPromise, errorPromise]);
  if (timeout) clearTimeout(timeout);

  if (exitCode !== 0) {
    throw new Error(
      [
        `Local command failed for ${context.finalSlug}: ${invocation.command} ${(invocation.args ?? []).join(' ')}`,
        `exitCode=${exitCode ?? 'null'} signal=${signal ?? 'null'}`,
        stderr.trim() ? `stderr: ${stderr.trim()}` : undefined,
      ]
        .filter(Boolean)
        .join('\n')
    );
  }

  return {
    command: invocation.command,
    args: invocation.args ?? [],
    stdout,
    stderr,
    exitCode,
    parsed: parseJsonIfRequested(stdout, execution.parseJson ?? false),
  };

View on GitHub (pinned to 64b1b85502)

Solutions

  1. Read the stderr line in the message — it is the downstream tool's actual error
  2. Fix the environmental cause (install helper, grant permissions, fix args)
  3. Check exitCode/signal: signal=SIGTERM often means a timeout kill
  4. Wrap the call and surface stderr to the user instead of the generic wrapper
Defensive patterns

Strategy: try-catch

Try / catch

try { const res = await executeLocalTool(slug, args, ctx); } catch (e) { if (e instanceof Error && e.message.includes('Local command failed')) { console.error(e.message); // includes stderr, exitCode, signal } }

Prevention

When it happens

Trigger: The external tool (e.g. sqlite3, imessage-export, a git helper) exits non-zero due to bad input, missing permissions, or missing runtime; the process is killed by SIGTERM/SIGKILL (timeout, OOM).

Common situations: Missing macOS Full Disk Access permission for terminal reading iMessage DB; the external helper binary not installed; sandboxed environments killing subprocesses; invalid arguments passed through to the child process.

Related errors


AI-assisted analysis of ComposioHQ/composio@64b1b85502 (2026-08-28). Data as JSON: /api/errors/1c9c5810777785ea. Report an issue: GitHub.