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
- Read the stderr line in the message — it is the downstream tool's actual error
- Fix the environmental cause (install helper, grant permissions, fix args)
- Check exitCode/signal: signal=SIGTERM often means a timeout kill
- 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
- Ensure helper binaries and OS permissions (Full Disk Access) exist
- Parse stderr from the message for actionable downstream errors
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
- Local tool ${resolution.finalSlug} is not supported on ${res
- Bundled binary ${value.bundledBinary} was not found for ${co
- ${command} failed with exitCode=${exitCode ?? 'null'} signal
- proxy() requires an authenticated Composio user session.
- Missing required flag: ${name}
AI-assisted analysis of ComposioHQ/composio@64b1b85502 (2026-08-28).
Data as JSON: /api/errors/1c9c5810777785ea.
Report an issue: GitHub.