Dagger TypeScript SDK 错误体系解析:FunctionNotFound 错误类的定义、触发场景与降级机制
【免费下载链接】daggerAutomation engine to build, test and ship any codebase. Runs locally, in CI, or directly in the cloud项目地址: https://gitcode.com/GitHub_Trending/da/dagger
本文聚焦 Dagger 项目 TypeScript SDK 中的FunctionNotFound错误类,结合官方 API 文档与 SDK 源码,系统讲解该错误的类定义、构造参数、属性与方法、底层实现,以及它在 Dagger 模块执行器与入口调用链中的真实触发场景和注册表降级机制。读完本文,你将掌握如何识别、捕获并程序化处理 Dagger 模块运行期的"函数/对象未找到"错误,并能理解整个 DaggerSDKError 错误码体系(D100~D110)的设计脉络。
一、文档定位:一个位于错误体系顶层的 API 引用页
在 Dagger 的版本化文档中,FunctionNotFound.md 是 TypeScript SDKcommon/errors模块的类参考页之一,它与DaggerSDKError、ExecError、GraphQLRequestError、UnknownDaggerError等十余个错误类共同构成了 SDK 的错误类型目录(完整清单见 common/errors 索引)。
该页面给出的核心 API 签名如下:
class FunctionNotFound extends DaggerSDKError { // 构造器 new FunctionNotFound(message: string, options?: DaggerSDKErrorOptions): FunctionNotFound // 属性 code: "D109" = ERROR_CODES.ExecError name: "ExecError" = ERROR_NAMES.ExecError message: string cause?: Error stack?: string // 方法 printStackTrace(): void }从类名即可看出,FunctionNotFound表达的是"函数未找到"这一语义——当 Dagger 在解析或调用某个模块对象及其方法时无法找到对应实现,就会抛出该错误。下面逐项拆解这个类。
二、继承关系与构造器
FunctionNotFound直接继承自DaggerSDKError(详见 DaggerSDKError 类文档)。在 SDK 源码中,这层关系由 FunctionNotFound.ts 体现:
import { DaggerSDKError, DaggerSDKErrorOptions } from "./DaggerSDKError.js" import { ERROR_CODES, ERROR_NAMES } from "./errors-codes.js" export class FunctionNotFound extends DaggerSDKError { name = ERROR_NAMES.ExecError code = ERROR_CODES.ExecError constructor(message: string, options?: DaggerSDKErrorOptions) { super(message, options) } }构造参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
message | string | 是 | 错误描述信息,由抛出方填充(例如Object foo not found) |
options? | DaggerSDKErrorOptions | 否 | 可选配置,目前仅支持cause字段,用于携带导致本次错误的原始Error |
DaggerSDKErrorOptions定义在 DaggerSDKError.ts:
export interface DaggerSDKErrorOptions { cause?: Error }构造函数内部直接调用基类构造函数:super(message, options),基类会执行super(message)并读取options?.cause赋值给实例的cause属性。
关于文档首行描述的一个细节
该参考页首行写着 "The base error. Every other error inherits this error."(基类错误,其他错误均继承自它)。从源码结构看,这句话实际与 DaggerSDKError.ts 中基类的 JSDoc 注释完全一致,属于 TypeDoc 自动生成文档时沿用的基类描述。真实情况是:DaggerSDKError才是整个错误体系的基础类,FunctionNotFound只是它的一个具体子类。阅读文档时留意这一点,可以避免对继承层级产生误解。
三、属性详解
3.1 name 与 code:被复用的错误标识
这是FunctionNotFound最值得注意的实现细节:它的name和code并不是专属值,而是直接复用了ExecError的常量:
name = ERROR_NAMES.ExecError // 实际值 "ExecError" code = ERROR_CODES.ExecError // 实际值 "D109"也就是说,从错误码角度,FunctionNotFound与ExecError共享"D109"与名称"ExecError"。这一点在 errors-codes.ts 中可以确认:
export const ERROR_CODES = { GraphQLRequestError: "D100", UnknownDaggerError: "D101", TooManyNestedObjectsError: "D102", EngineSessionConnectParamsParseError: "D103", EngineSessionConnectionTimeoutError: "D104", EngineSessionError: "D105", InitEngineSessionBinaryError: "D106", DockerImageRefValidationError: "D107", NotAwaitedRequestError: "D108", ExecError: "D109", IntrospectionError: "D110", } as const注意ERROR_CODES被声明为as const,因此每个值都是字面量类型;ERROR_NAMES则通过Object.keys(ERROR_CODES)反向生成,保证名称与码表一一对应。这意味着你在代码中看到error.code === "D109"时,它既可能来自ExecError,也可能来自FunctionNotFound——不要仅凭错误码区分这两类错误,建议配合instanceof判断具体类型。
3.2 message 与 stack
message: string:继承自Error,保存构造时传入的错误描述。stack?: string:标准 V8 堆栈信息,同样继承自基类。可选属性的语义与原生Error.stack一致,仅在 V8 生成堆栈后可用。
3.3 cause?: Error
cause是DaggerSDKError体系引入的"原始错误"承载字段:
/** * The original error, which caused the DaggerSDKError. */ cause?: Error它用于错误链(error chaining):当 SDK 在捕获到某个底层错误后需要包装为 Dagger 语义错误时,可将原始错误挂到cause上,方便调试时追溯根因。FunctionNotFound自身并未重写该字段,完全继承自基类,因此它是可选的(optional)。
四、方法:printStackTrace()
FunctionNotFound继承自基类的唯一公开方法printStackTrace(): void,用于"漂亮地打印"错误堆栈。其实现位于 DaggerSDKError.ts:
printStackTrace() { log(this.stack) }它调用 SDK 内部的log工具函数输出this.stack。该方法的可见性为公开(TypeDoc 标记为@hidden的Symbol.toStringTaggetter 除外),便于在捕获错误后快速在控制台定位调用链。
五、触发场景:模块执行器中的三处抛错点
FunctionNotFound的实际抛出位置集中在 executor.ts,它承担着 Dagger TypeScript 模块运行期"按名解析对象与方法并执行"的核心职责:
| 抛错点 | 方法 | 消息模板 | 触发条件 |
|---|---|---|---|
| 第一处 | getExportedObject | `Object ${object} not found` | 在当前已加载的所有Module中找不到导出对象 |
| 第二处 | buildClass | `Object ${object} not found in the module` | 在模块的 introspection 对象表中找不到该对象定义 |
| 第三处 | getResult | `Method ${method} not found` | 对象存在,但实例上不存在要调用的方法 |
对应源码摘录:
// executor.ts - getExportedObject const module = this.modules.find((m) => m[key] !== undefined) if (!module) { throw new FunctionNotFound(`Object ${object} not found`) } // executor.ts - buildClass const daggerObject = this.daggerModule.objects[object] if (!daggerObject) { throw new FunctionNotFound(`Object ${object} not found in the module`) } // executor.ts - getResult const builtObj = this.buildClass(object, state) if (!builtObj[method]) { throw new FunctionNotFound(`Method ${method} not found`) }从源码结构可以推断出典型触发场景:当外部调用方通过 GraphQL 请求一个不存在的 Dagger 对象(例如dag.getFoo()),或请求对象上并不存在的方法(例如对某个@object()装饰的类调用未用@func()暴露的方法)时,执行器就会以FunctionNotFound中断调用。这是模块 API 与实现不匹配时的第一道防线。
六、关键机制:invoke 入口的注册表降级
FunctionNotFound并非只用于"直接失败",它还在 invoke.ts 中扮演了降级切换的触发信号角色。入口函数invoke在调用executor.getResult时包裹了 try/catch:
try { result = await executor.getResult(object.name, method.name, parentState, args) } catch (e) { // If the function isn't found because it's // not exported, we try to get the result from the registry. if (e instanceof FunctionNotFound) { result = await registry.getResult(object.name, method.name, parentState, args) } else { throw e } }其逻辑可归纳为:
- 优先尝试从模块的导出对象(
executor持有的Module[])中解析并调用函数; - 若抛出
FunctionNotFound,说明该函数未通过模块导出,则转而查询registry(registry.ts); registry是由@object()装饰器注册的类与方法的仓储结构,内部以 map 作为数据结构以优化查找性能(见 registry.ts 中的RegistryClass定义);- 只有
instanceof FunctionNotFound才会触发降级,其他错误一律原样上抛——这保证了降级路径的精确性。
换句话说,FunctionNotFound在 Dagger 模块运行模型中承担"双轨解析"的哨兵职责:导出解析失败时,它作为可预期信号驱动注册表回退,避免模块中大量通过装饰器注册但未显式导出的函数无法被调用。这也是该错误类与ExecError共享错误码却仍有独立类名的现实意义——运行时依赖instanceof而非code做分支判断。
七、实战:如何识别与处理 FunctionNotFound
7.1 程序化识别
文档明确说明code的用途是 "Use this to identify dagger errors programmatically"(用错误码在程序中识别 Dagger 错误)。结合上文,推荐的识别姿势是instanceof优先:
import { FunctionNotFound, ExecError } from "@dagger.io/dagger/common/errors" try { const result = await someModuleFunction() } catch (e) { if (e instanceof FunctionNotFound) { // 对象或方法未找到,可提示调用方检查 API 名称 console.error(`module resolution failed: ${e.message}`) e.printStackTrace() } else if (e instanceof ExecError) { // 真正的执行期错误(注意两者 code 均为 "D109") console.error(`exec error: ${e.message}`, { cause: e.cause }) } else { throw e } }如果需要跨版本兼容或无法直接import具体类,可退而使用错误码:if ((e as DaggerSDKError).code === "D109")。但要牢记 §3.1 的结论——"D109"同时属于ExecError与FunctionNotFound,仅凭码无法区分两者。
7.2 调试建议
- 查看
message:三种模板(Object xxx not found、Object xxx not found in the module、Method xxx not found)直接指示是对象级还是方法级解析失败; - 调用
printStackTrace()输出完整调用链; - 检查
cause:若错误由其他底层异常包装而来,cause中保存原始Error; - 若出现"导出解析失败但注册表降级也未命中"的情况,请核对模块是否用
@object()/@func()正确装饰类与方法(相关装饰器语义见 registry.ts 中的FunctionOptions,其中alias可用于为函数设置对外暴露的别名,cache可控制函数的缓存策略)。
八、在错误体系中的位置
FunctionNotFound属于 common/errors 目录 下 12 个错误类之一,整个体系的骨架是:
Error └─ DaggerSDKError (抽象基类,含 name / code / cause / printStackTrace) ├─ FunctionNotFound (code D109,与 ExecError 共用) ├─ ExecError (code D109) ├─ GraphQLRequestError (code D100) ├─ UnknownDaggerError (code D101) ├─ TooManyNestedObjectsError (code D102) ├─ EngineSessionConnectParamsParseError (code D103) ├─ EngineSessionConnectionTimeoutError (code D104) ├─ EngineSessionError (code D105) ├─ InitEngineSessionBinaryError (code D106) ├─ DockerImageRefValidationError (code D107) ├─ NotAwaitedRequestError (code D108) └─ IntrospectionError (code D110)所有错误类的名称与错误码统一定义在 errors-codes.ts,由as const保证类型安全,ERROR_NAMES由码表键名自动推导。理解这张表,你就掌握了整个 Dagger TypeScript SDK 错误信号的"字母表"——D100至D110覆盖了从 GraphQL 请求、引擎会话建立、镜像引用校验、执行错误到模块内省失败的各类故障域,而FunctionNotFound正是其中负责"模块函数解析"一环的关键成员。
参考文件索引
- 类 API 文档:FunctionNotFound.md
- 错误模块索引:common/errors/README.md
- 类实现:FunctionNotFound.ts
- 基类实现:DaggerSDKError.ts
- 错误码表:errors-codes.ts
- 抛错点:executor.ts
- 注册表降级:invoke.ts
- 注册表实现:registry.ts
【免费下载链接】daggerAutomation engine to build, test and ship any codebase. Runs locally, in CI, or directly in the cloud项目地址: https://gitcode.com/GitHub_Trending/da/dagger
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考