Add resilient error handling for Composio tool execution

Tool execution now returns actionable error messages when a connected
account is missing, directing the admin to set up the connection. Tool
loading is resilient to API failures, falling back to cached tools if
available instead of crashing the agent run.
This commit is contained in:
ZacharyZhang-NY committed 2026-06-30 21:59:39 -04:00
1 parent e83f379143
commit 81aae82211
1 file changed
+25 -13
@@ -48,11 +48,15 @@ export async function loadComposioTools(db: DatabaseSync, orgId: string): Promis
if (cached && cached.toolkitsKey === toolkitsKey && Date.now() - cached.fetchedAt < CACHE_TTL_MS) {
return cached.tools;
}
const client = new ComposioClient(apiKey);
const schemas = await client.getTools(toolkits);
const tools = schemas.map(composioSchemaToToolDefinition);
toolCache.set(orgId, { tools, fetchedAt: Date.now(), toolkitsKey });
return tools;
try {
const client = new ComposioClient(apiKey);
const schemas = await client.getTools(toolkits);
const tools = schemas.map(composioSchemaToToolDefinition);
toolCache.set(orgId, { tools, fetchedAt: Date.now(), toolkitsKey });
return tools;
} catch {
return cached?.tools ?? [];
}
}
export async function executeComposioTool(
@@ -61,17 +65,25 @@ export async function executeComposioTool(
call: ToolCall,
): Promise<string> {
const apiKey = getComposioApiKey(db, orgId);
if (!apiKey) return "ERROR: Composio API key is not configured.";
if (!apiKey) return "ERROR: Composio API key is not configured. Ask the admin to set it in Settings > Tools.";
const toolName = originalToolName(call.name);
const entityId = getComposioEntityId(db, orgId);
const client = new ComposioClient(apiKey);
const result = await client.executeTool({
toolName,
arguments: call.arguments,
entityId,
});
if (result.error) return `ERROR: ${result.error}`;
return JSON.stringify(result.data);
try {
const result = await client.executeTool({
toolName,
arguments: call.arguments,
entityId,
});
if (result.error) return `ERROR: ${result.error}`;
return JSON.stringify(result.data);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
if (message.includes("ConnectedAccount") || message.includes("connected account") || message.includes("entity_id")) {
return `ERROR: No connected account for this tool. The admin needs to connect the service at https://app.composio.dev before agents can use it.`;
}
return `ERROR: ${message}`;
}
}
function getComposioApiKey(db: DatabaseSync, orgId: string): string | null {