Composio TypeScript SDK 错误处理完全指南:错误层次结构、捕获策略与用户友好的格式化输出
2026/9/12 1:35:28 网站建设 项目流程

Composio TypeScript SDK 错误处理完全指南:错误层次结构、捕获策略与用户友好的格式化输出

【免费下载链接】composioComposio powers 1000+ toolkits, tool search, context management, authentication, and a sandboxed workbench to help you build AI agents that turn intent into action.项目地址: https://gitcode.com/GitHub_Trending/co/composio

导读

在基于 Composio SDK 构建 AI Agent 应用时,工具执行、连接授权、输入校验等环节都会产生可预期的失败路径。Composio TypeScript SDK 提供了一套完整的错误体系:以ComposioError为基类,衍生出覆盖工具执行、认证配置、连接请求、校验失败等场景的子类,并内置了带颜色格式化的prettyPrint()、统一的handle()等错误展示工具。本文将围绕 ts/docs/advanced/error-handling.md 的完整内容,结合@composio/core包的源码实现,系统讲解如何识别、捕获、分类与展示这些错误,最终帮助你写出健壮、可观测、对用户友好的 Agent 应用。

错误层次结构(Error Hierarchy)

Composio SDK 采用"单一基类 + 领域子类"的结构化错误体系。所有错误最终都继承自ComposioError,而ComposioError本身继承自 JavaScript 内置的Error。从源码 ComposioError.ts 可以看到完整的继承树:

  • ComposioError:所有 Composio 错误的基类
    • AuthConfigErrors:与认证配置(Auth Config)相关的错误,例如ComposioAuthConfigNotFoundError
    • ConnectedAccountsError:与已连接账号相关的错误,例如ComposioConnectedAccountNotFoundError
    • ConnectionRequestError:与连接请求(OAuth 授权流程)相关的错误,例如ConnectionRequestTimeoutErrorConnectionRequestFailedError
    • ToolErrors:与工具及其执行相关的错误,例如ComposioToolNotFoundErrorComposioToolExecutionErrorComposioToolVersionRequiredErrorComposioInvalidToolArgumentsError
    • ToolkitErrors:与工具包(Toolkit)相关的错误,例如ComposioToolkitNotFoundErrorComposioToolkitFetchError
    • ValidationError:与输入校验相关的错误

所有错误类都通过 errors/index.ts 统一从@composio/core导出,你只需要一条 import 语句即可按需引用。

ComposioError 基类的核心字段

在 ComposioError.ts 中,ComposioError在原生Error之上增加了以下结构化字段:

字段类型说明
codestring错误分类代码,构造时自动加上TS-SDK::前缀,例如TS-SDK::TOOL_NOT_FOUND
statusCodenumber关联的 HTTP 状态码;若causeBadRequestError,会自动继承其status
causeunknown底层原因,可以是原生Error、ZodError 或任意值
metaRecord<string, unknown>附加元数据,便于携带上下文信息
possibleFixesstring[]建议的修复措施列表,会直接展示给用户
errorIdstring错误标识(可选)
stackstring合并后的堆栈:当包装底层错误时,combineStackTraces会把原始堆栈与包装堆栈拼接,并以Caused by:分隔(见 ComposioError.ts)

实现细节:ComposioError使用definePropertyIfExists只为有值的属性定义可枚举属性(ComposioError.ts),因此未设置的字段不会以undefined的形式出现在错误输出中,保证展示信息干净。

常见错误类型与捕获示例

输入校验错误(ValidationError)

当传给 SDK 方法的输入不符合预期的 Zod Schema 时,会抛出ValidationError。它内部包装了一个ZodError,并把校验问题逐条映射到possibleFixes(见 ValidationErrors.ts):

try { await composio.tools.get('default', { invalidParam: 'value', // 触发校验错误 }); } catch (error) { if (error instanceof ValidationError) { console.error('Validation error:', error.message); console.error('Validation details:', error.validationError); } }

值得注意的源码细节:ValidationError的构造函数会调用generateUserFriendlyMessage()(ValidationErrors.ts),当底层 Zod issue 是invalid_type时,会自动生成类似The owner should be a string, but you provided a number这样易于理解的提示,并追加到message中。因此即使你不做任何额外加工,error.message本身已经足够友好。

同文件还定义了与 Schema 转换相关的JsonSchemaToZodErrorJsonSchemaRefResolutionError(分别对应JSON_SCHEMA_TO_ZOD_ERRORJSON_SCHEMA_REF_RESOLUTION_ERROR错误码),在自定义工具 Schema 转换失败时抛出。

工具执行错误(ComposioToolExecutionError)

工具执行期间发生的错误会被包装为ComposioToolExecutionError(ToolErrors.ts),并携带toolSlug、请求体等上下文:

try { const result = await composio.tools.execute('GITHUB_GET_REPO', { userId: 'default', arguments: { owner: 'composio', // 缺少 'repo' 参数会触发错误 }, }); } catch (error) { if (error instanceof ComposioToolExecutionError) { console.error('Tool execution error:', error.message); console.error('Tool:', error.context.toolSlug); console.error('Execution params:', error.context.body); } }

从源码可以进一步了解其底层映射逻辑:SDK 在捕获 API 层错误时,会调用handleToolExecutionError(tool, actualError)(ToolErrors.ts)。该函数会读取服务端返回的错误体(ComposioAPIServerErrorBody),若错误码命中ERROR_CODE_HANDLERS映射表(目前 1803 对应ComposioConnectedAccountNotFoundError),则返回对应的具体错误类型;否则回退为通用的ComposioToolExecutionError,并把原始错误作为cause保留。

资源未找到错误(ComposioToolNotFoundError)

当请求不存在的工具时,抛出ComposioToolNotFoundError(ToolErrors.ts),错误码为TS-SDK::TOOL_NOT_FOUND

try { await composio.tools.get('default', 'NON_EXISTENT_TOOL'); } catch (error) { if (error instanceof ComposioToolNotFoundError) { console.error('Tool not found:', error.message); } }

同类别的工具错误还包括:ComposioProviderNotDefinedErrorComposioInvalidModifierErrorComposioInvalidToolArgumentsErrorComposioInvalidExecuteFunctionErrorComposioGlobalExecuteToolFnNotSetError,以及一个值得在生产环境重点关注的ComposioToolVersionRequiredError

工具版本缺失错误(ComposioToolVersionRequiredError)

该错误是"手动执行工具时未指定 toolkit 版本"才会出现的场景:当解析到的版本为latest且未显式设置dangerouslySkipVersionCheck时,tools.execute()会抛出此错误(见 ToolErrors.ts 的 JSDoc 示例)。修复方式有四种,按推荐程度排列:

// 方案一:在 execute 调用中显式传入版本 await composio.tools.execute('GITHUB_GET_REPOS', { userId: 'default', version: '20250909_00', arguments: { owner: 'composio' }, }); // 方案二:在 SDK 初始化时配置 toolkitVersions const composio = new Composio({ toolkitVersions: { github: '20250909_00' }, }); // 方案三:使用环境变量 COMPOSIO_TOOLKIT_VERSION_<TOOLKIT_SLUG> // COMPOSIO_TOOLKIT_VERSION_GITHUB=20250909_00 // 方案四:跳过版本检查(仅建议在非生产环境使用) await composio.tools.execute('GITHUB_GET_REPOS', { userId: 'default', dangerouslySkipVersionCheck: true, arguments: { owner: 'composio' }, });

SDK 级错误

ComposioNoAPIKeyError(SDKErrors.ts)在 SDK 无法从参数、环境变量(COMPOSIO_API_KEY)或用户配置文件中找到 API Key 时抛出,默认错误码为TS-SDK::NO_API_KEY_PROVIDED、状态码 401,并附上三条修复建议。ComposioRequestCancelledError(SDKErrors.ts)则对应调用方通过AbortSignal主动取消请求的场景:

try { await composio.tools.execute(slug, body, { signal: AbortSignal.timeout(5_000) }); } catch (err) { if (err instanceof ComposioRequestCancelledError) return; // 调用方主动取消,属预期行为 throw err; }

SDK 还导出了isRequestAbortError()辅助函数(SDKErrors.ts),它能够穿透多层cause链识别各类 Abort 错误,对 dual-package 场景做了兜底判断,适合在中间件层统一识别"取消"语义。

处理工具执行中的双层错误

执行工具时,需要同时处理两种错误形态:SDK 抛出的异常(连接失败、鉴权失败等),以及执行结果中的业务失败。Composio 的execute返回对象带有successful标志,即便工具在远端执行失败,SDK 调用本身也可能正常返回:

try { const result = await composio.tools.execute('GITHUB_GET_REPO', { userId: 'default', arguments: { owner: 'composio', repo: 'sdk', }, }); // 先检查执行是否成功 if (result.successful) { console.log('Repository details:', result.data); } else { // 处理"执行失败但 SDK 调用成功返回"的情况 console.error('Execution failed:', result.error); } } catch (error) { // 处理 SDK 层异常 console.error('SDK error:', error.message); }

这一模式也是官方文档强调的 Best Practice 之一:永远不要假定execute()只通过异常表达失败result.successful === false时,result.error中已经包含了结构化的失败信息,直接透传即可。

连接流程中的错误处理

连接第三方账号(OAuth 授权)涉及"发起授权 → 等待连接建立"两个阶段,两个阶段都可能失败:

try { // 第一步:发起授权请求 const connectionRequest = await composio.toolkits.authorize('user123', 'github'); // 第二步:等待连接建立(60 秒超时) try { const connectedAccount = await composio.connectedAccounts.waitForConnection( connectionRequest.id, 60000 // 60 second timeout ); console.log('Connected account:', connectedAccount); } catch (timeoutError) { if (timeoutError instanceof ConnectionRequestTimeoutError) { console.error('Connection timed out. Please try again.'); } else if (timeoutError instanceof ConnectionRequestFailedError) { console.error('Connection failed:', timeoutError.message); } } } catch (error) { if (error instanceof ComposioAuthConfigNotFoundError) { console.error('Auth config not found:', error.message); } else { console.error('Error initiating connection:', error.message); } }

源码层面,ConnectionRequestTimeoutErrorConnectionRequestFailedError定义在 ConnectionRequestErrors.ts,错误码分别为TS-SDK::CONNECTION_REQUEST_TIMEOUTTS-SDK::CONNECTION_REQUEST_FAILEDComposioAuthConfigNotFoundError定义在 AuthConfigErrors.ts,错误码为TS-SDK::AUTH_CONFIG_NOT_FOUND,其possibleFixes内置了"检查 auth config 是否存在 / id 是否正确 / 是否启用"三条排查建议。建议为waitForConnection设置合理的超时时间(文档示例为 60 秒),避免调用无限挂起。

全局错误处理器:集中式的错误分类与展示

对于规模较大的应用,可以定义一个集中式的错误处理函数,统一对所有错误分类处理。官方文档给出了一个完整的handleComposioError示例,它按"具体子类 → 基类 → 兜底"的顺序做instanceof判断:

function handleComposioError(error: unknown): void { if (error instanceof ValidationError) { console.error('Validation error:', error.message); } else if (error instanceof ComposioToolNotFoundError) { console.error('Tool not found:', error.message); } else if (error instanceof ComposioToolExecutionError) { console.error('Tool execution error:', error.message); } else if (error instanceof ComposioAuthConfigNotFoundError) { console.error('Auth config not found:', error.message); } else if (error instanceof ConnectionRequestFailedError) { console.error('Connection failed:', error.message); } else if (error instanceof ConnectionRequestTimeoutError) { console.error('Connection timed out:', error.message); } else if (error instanceof ComposioError) { console.error('Composio error:', error.message); } else { console.error('Unexpected error:', error); } } try { const result = await composio.tools.execute('GITHUB_GET_REPO', { userId: 'default', arguments: { owner: 'composio', repo: 'sdk' }, }); if (!result.successful) { console.error('Execution failed:', result.error); } } catch (error) { handleComposioError(error); }

注意判断顺序:必须把更具体的子类放在前面,把ComposioError基类放在后面,否则基类分支会吞掉所有具体错误信息。

Session 自定义工具中的错误处理(Tool Router Custom Tools)

在使用 Tool Router 创建会话自定义工具时,handler 内部如果无法继续执行,直接抛出普通Error即可。SDK 会把抛出的错误包装进标准的会话执行响应中,无需自行处理响应格式。experimental_createTool@composio/core导出(见 experimental/index.ts):

import { experimental_createTool } from '@composio/core'; import { z } from 'zod'; const customTool = experimental_createTool('MY_CUSTOM_TOOL', { name: 'My Custom Tool', description: 'A custom tool with error handling', inputParams: z.object({ param1: z.string().describe('Required parameter'), }), execute: async (input) => { const { param1 } = input; if (param1.trim() === '') { throw new Error('param1 cannot be empty'); } const result = await someExternalService(param1); return { result }; }, });

这种设计让自定义工具与内置工具的错误语义保持一致:Agent 侧看到的是统一格式的失败响应,便于 LLM 理解并修正参数后重试。

用户友好的错误展示

Composio SDK 内置了带颜色与格式的错误输出能力,底层依赖picocolors进行终端着色,所有输出统一走logger.error(实现见 ComposioError.ts)。

使用 toString()

ComposioError及其子类的toString()返回格式化的错误字符串表示:

try { // 可能失败的操作 } catch (error) { if (error instanceof ComposioError) { // 输出带颜色的格式化错误信息 console.error(error.toString()); } }

使用 prettyPrint()

prettyPrint()提供更美观的错误展示,直接输出到console.error。从源码看,它会按区块渲染:红色背景的ERROR标识与加粗消息、黄色错误码与状态码、灰色ReasonAdditional Information(JSON 格式化缩进)、青色Try the following:修复建议列表,以及可选的堆栈追踪:

try { // 可能失败的操作 } catch (error) { if (error instanceof ComposioError) { error.prettyPrint(); // 基础展示 error.prettyPrint(true); // 包含堆栈追踪 // 重要:prettyPrint 之后不要再重复 log 或直接 re-throw, // 否则控制台会出现重复的错误信息 } }

使用静态 handle() 工具

ComposioError.handle(error, options)(ComposioError.ts)是官方推荐的统一入口。从源码可以看到它的完整分派逻辑:

  • 错误是ComposioError→ 调用prettyPrint(includeStack)
  • 错误是ZodError→ 调用handleZodError,逐条列出Invalid parametersExpected parameters,对 LLM 或调试者非常友好;
  • 错误是普通Error→ 调用handleStandardError做同风格的基础格式化;
  • 其余未知值 → 调用handleUnknownError优雅兜底。
try { // 可能失败的操作 } catch (error) { // 统一处理所有类型错误,自动格式化 ComposioError.handle(error); // 包含堆栈追踪 ComposioError.handle(error, { includeStack: true }); }

使用 handleAndThrow() 处理致命错误

对于必须终止执行的致命错误,使用handleAndThrow():先按handle()的格式展示错误,再原样抛出。源码中该方法签名返回never类型(ComposioError.ts),TypeScript 编译器会因此识别调用点之后的代码不可达:

try { // 可能失败的操作 } catch (error) { // 展示错误后抛出(适用于致命错误) ComposioError.handleAndThrow(error); // 抛出的同时包含堆栈追踪 ComposioError.handleAndThrow(error, true); }

process.exit()不同,handleAndThrow只是"展示 + 抛出",因此它兼容 Serverless 环境——错误可以继续向上冒泡交给运行时或上层框架处理,而不会在函数内部直接终止进程。

一步完成创建与打印:createAndPrint()

静态工厂方法createAndPrint(message, options, includeStack?)会创建错误、调用prettyPrint并返回错误实例(ComposioError.ts),适合在自定义错误处理器或格式化器中直接使用:

// 创建、打印并抛出错误 throw ComposioError.createAndPrint('Something went wrong', { code: 'CUSTOM_ERROR', cause: 'The operation failed because of XYZ', possibleFixes: ['Try solution A', 'Try solution B'], });

最佳实践清单

结合官方文档与源码实现,构建健壮的错误处理流程时建议遵循以下原则:

  1. 调用 SDK 方法时始终使用 try/catch,不要依赖未捕获异常;
  2. 工具执行后检查result.successful,区分"SDK 层异常"与"业务执行失败"两种形态;
  3. 对不同错误类型提供针对性处理instanceof判断时把具体子类放在基类之前;
  4. 记录详细错误信息,充分利用error.codeerror.causeerror.meta、合并堆栈等结构化字段辅助排查;
  5. 面向用户展示友好消息,优先使用prettyPrint()/handle()的格式化输出,或基于possibleFixes生成修复提示;
  6. waitForConnection等阻塞操作设置合理超时,避免挂起;
  7. 调用 SDK 前自行校验输入,减少不必要的远端往返;
  8. 对瞬时错误实现重试逻辑,例如网络抖动、超时类错误。

导入错误类与自定义错误

所有错误类均从@composio/core主包导出(见 errors/index.ts),导入方式如下:

import { ComposioError, ComposioNoAPIKeyError, ComposioToolNotFoundError, ValidationError, } from '@composio/core';

你还可以把 SDK 的错误处理工具集成到应用自身的集中错误处理流程中,例如按环境决定是否包含堆栈:

import { ComposioError } from '@composio/core'; // 集中式错误处理器 function handleApplicationError(error: unknown) { // 使用内置错误处理工具 ComposioError.handle(error, { includeStack: process.env.NODE_ENV === 'development', }); // 追加应用自定义处理逻辑,例如上报监控服务 } try { // 应用代码 } catch (error) { handleApplicationError(error); }

自定义错误类型

如果需要融入 Composio 错误体系,可以继承ComposioError并传入codepossibleFixes等选项。构造时基类会自动为code添加TS-SDK::前缀,handle()也能自动识别并格式化你的自定义子类:

import { ComposioError } from '@composio/core'; class MyCustomError extends ComposioError { constructor(message: string) { super(message, { code: 'MY_CUSTOM_ERROR', possibleFixes: [ 'Check your application configuration', 'Ensure all required dependencies are installed', ], }); this.name = 'MyCustomError'; } } try { // 某些条件 if (!config.isValid) { throw new MyCustomError('Invalid configuration'); } } catch (error) { ComposioError.handle(error); // 自动识别并格式化 MyCustomError }

结语

Composio TS SDK 的错误体系设计呈现出两个鲜明特点:一是结构化code/statusCode/cause/meta/possibleFixes字段让错误不再是一段难以解析的字符串,而是可直接用于诊断、重试与上报的数据;二是面向用户prettyPrint()handle()handleAndThrow()createAndPrint()等工具让终端输出具备可读性与可操作性。理解并善用这套机制(错误基类、工具错误、校验错误、连接请求错误),再配合"检查result.successful+ 全局分类处理 + 合理超时重试"的组合拳,就能显著提升 Agent 应用在生产环境中的稳定性与可维护性。

【免费下载链接】composioComposio powers 1000+ toolkits, tool search, context management, authentication, and a sandboxed workbench to help you build AI agents that turn intent into action.项目地址: https://gitcode.com/GitHub_Trending/co/composio

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询