Dagger TypeScript SDK 错误处理详解:ExecError 类的属性、源码与实战捕获
【免费下载链接】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 中ExecError类(API 参考见 ExecError.md)为核心,介绍当容器流水线(pipeline)中的exec操作失败时,SDK 如何把底层命令退出信息包装成可编程处理的错误对象。读完本文,你将掌握ExecError的全部属性语义、错误码D109的含义、SDK 内部抛出该错误的底层机制,以及如何在 TypeScript 项目中精准捕获并诊断容器命令执行失败。
ExecError 是什么:流水线 exec 操作的 API 错误
在 Dagger 中,构建、测试、发布任何代码库的核心动作大多是对容器执行命令(withExec),当某个容器命令以非零退出码结束,Dagger 引擎会将其标记为EXEC_ERROR类型,Dagger TypeScript SDK 在收到该 GraphQL 响应后会抛出ExecError。
从类型体系上看,ExecError直接继承自抽象基类DaggerSDKError,后者是所有 Dagger 错误的父类:
- 基类定义了
name与code两个抽象只读属性,要求每个子类提供 Dagger 专属的错误名称与错误码; - 基类持有
cause(引发错误的原始 Error)、message与stack; - 基类还提供
printStackTrace()方法用于将堆栈打印到控制台(源码见 DaggerSDKError.ts)。
ExecError的定义位于 ExecError.ts,其类签名与构造函数如下:
export class ExecError extends DaggerSDKError { name = ERROR_NAMES.ExecError code = ERROR_CODES.ExecError cmd: string[] exitCode: number stdout: string stderr: string extensions?: GraphQLErrorExtensions constructor(message: string, options: ExecErrorOptions) { super(message, options) this.cmd = options.cmd this.exitCode = options.exitCode this.stdout = options.stdout this.stderr = options.stderr this.extensions = options.extensions } }其中ExecErrorOptions在DaggerSDKErrorOptions(仅含cause)基础上,要求调用方必须提供cmd、exitCode、stdout、stderr,并可附带 GraphQL 扩展信息extensions。
属性全解:从命令到输出的完整诊断信息
ExecError携带了排障所需的全部现场信息,每个字段都与一次失败的 exec 操作一一对应。
cmd:导致错误的命令
cmd: string[]以字符串数组形式保存触发出错的完整命令及其参数,例如["sh", "-c", "cat /testout >&1; cat /testerr >&2; exit 127"]。注意它是参数数组而非拼接好的单行字符串,便于你直接在错误处理中重建命令或做日志脱敏。
exitCode:命令退出码
exitCode: number容器内命令的退出码。非零退出码(如127表示命令不存在、1表示通用运行失败)是 exec 报错的直接原因。在 SDK 内部,当引擎未提供该字段时会回退为-1。
stdout 与 stderr:标准输出与标准错误
stdout: string stderr: string命令执行期间产生的标准输出与标准错误内容,是定位问题最直接的日志来源。在 SDK 实现中,若引擎未返回对应字段,会回退为空字符串""。
值得注意的一个细节:stdout/stderr不会混入message,测试用例专门断言了e.toString()与e.message均不包含 stdout/stderr 内容(见 api.spec.ts),因此你可以安全地把message作为简短摘要展示,而把完整输出单独记录。
extensions:GraphQL 错误扩展信息
extensions?: any透传 GraphQL 响应中附带的extensions对象。SDK 正是通过其中的_type === "EXEC_ERROR"来识别 exec 失败并构造ExecError的(详见下文“源码机制”一节)。
name、code、cause、message、stack
| 属性 | 类型 | 说明 | 来源 |
|---|---|---|---|
name | "ExecError" | Dagger 错误名称,值来自ERROR_NAMES.ExecError | 覆盖基类 |
code | "D109" | Dagger 专属错误码,值来自ERROR_CODES.ExecError | 覆盖基类 |
cause | Error(可选) | 引发错误的原始 Error 对象 | 继承自DaggerSDKError |
message | string | 错误摘要信息 | 继承自Error |
stack | string(可选) | 调用堆栈 | 继承自Error |
方法:printStackTrace()
printStackTrace(): void继承自DaggerSDKError,调用内部日志工具打印this.stack(见 DaggerSDKError.ts),适合在 catch 分支中快速输出堆栈定位调用链。
错误码体系:D109 在整个 Dagger 错误表中的位置
Dagger TypeScript SDK 为每一类错误分配了稳定的字符串错误码,便于程序化区分错误类型,统一定义在 errors-codes.ts:
| 错误码 | 错误类 |
|---|---|
D100 | GraphQLRequestError |
D101 | UnknownDaggerError |
D102 | TooManyNestedObjectsError |
D103 | EngineSessionConnectParamsParseError |
D104 | EngineSessionConnectionTimeoutError |
D105 | EngineSessionError |
D106 | InitEngineSessionBinaryError |
D107 | DockerImageRefValidationError |
D108 | NotAwaitedRequestError |
D109 | ExecError |
D110 | IntrospectionError |
ExecError的name与code均在类初始化时通过ERROR_NAMES.ExecError、ERROR_CODES.ExecError赋值,因此每个实例天然携带D109标识。所有错误类统一从 index.ts 导出,你可以用以下方式按错误码或错误类型做分支处理:
import { ExecError, ERROR_CODES } from "@dagger.io/dagger" catch (e) { if (e instanceof ExecError) { console.error(`Dagger exec failed with code ${e.code}`) // "D109" } // 或按错误码判断 if ((e as any).code === ERROR_CODES.ExecError) { /* ... */ } }源码机制:ExecError 是在哪里被抛出的
ExecError并非在容器执行现场直接抛出,而是由 SDK 的 GraphQL 查询层在收到引擎错误响应后统一构造。关键调用链位于 compute_query.ts 的compute函数:
} catch (e: any) { if (e instanceof ClientError) { const msg = e.response.errors?.[0]?.message ?? `API Error` const ext = e.response.errors?.[0]?.extensions if (ext?._type === "EXEC_ERROR") { throw new ExecError(msg, { cmd: (ext.cmd as string[]) ?? [], exitCode: (ext.exitCode as number) ?? -1, stdout: (ext.stdout as string) ?? "", stderr: (ext.stderr as string) ?? "", extensions: ext, }) } throw new GraphQLRequestError(msg, { error: e, cause: e }) } // ... }机制要点:
- SDK 通过 GraphQL 客户端请求引擎,若返回
ClientError,则读取首个 GraphQL error 的message与extensions; - 当
extensions._type === "EXEC_ERROR"时,从扩展字段中提取cmd、exitCode、stdout、stderr并构造ExecError抛出——这就是实例属性与 GraphQL 扩展字段的对应来源; - 若错误类型不是 exec 失败,则回退抛出
GraphQLRequestError;连接被拒(ECONNREFUSED)时抛出NotAwaitedRequestError(提示函数未被await);其余情况抛出UnknownDaggerError。
这一设计意味着:凡是容器内命令执行失败(例如withExec后调用sync()),你捕获到的几乎都是ExecError,且其cmd、exitCode、stdout、stderr字段与引擎返回一一对应。
实战捕获:从测试用例看正确的处理姿势
官方测试 api.spec.ts 中“Return custom ExecError”用例演示了完整的触发与校验流程:
const stdout = "STDOUT HERE" const stderr = "STDERR HERE" const args = ["sh", "-c", "cat /testout >&1; cat /testerr >&2; exit 127"] await connect(async (client: Client) => { const ctr = client .container() .from("alpine:3.16.2") .withDirectory("/", client.directory() .withNewFile("testout", stdout) .withNewFile("testerr", stderr)) .withExec(args) try { await ctr.sync() } catch (e) { if (e instanceof ExecError) { assert(e.message.includes("exit code: 127")) assert.strictEqual(e.exitCode, 127) assert.strictEqual(e.stdout, stdout) assert.strictEqual(e.stderr, stderr) assert(!e.toString().includes(stdout)) assert(!e.message.includes(stderr)) } else { throw e } } })由该用例可提炼出三条实战经验:
- 捕获方式:优先使用
e instanceof ExecError做类型守卫,避免用any断言丢失类型信息;sync()是触发容器真正执行的入口,错误在此处抛出; - 信息一致性:
exitCode、stdout、stderr与容器内真实行为完全一致,可直接作为 CI 失败诊断的依据;message中会包含类似exit code: 127的摘要,但不会混入完整 stdout/stderr,适合展示给用户; - 未命中的兜底:如果 exec 失败但捕获到的不是
ExecError(例如请求层异常),应重新抛出或记录原始错误,避免吞掉异常。
另一个用例(Support container sync)则验证了assert.rejects(base.withExec(["foobar"]).sync(), ExecError),说明即使是很短的命令(如不存在的foobar)也会以ExecError形式暴露,捕获路径是稳定一致的。
常见误用与排查建议
- 在非 await 场景下误判错误类型:如果异步调用未被
await,错误可能以NotAwaitedRequestError(D108)出现而不是ExecError,先检查调用链是否完整await; - 日志脱敏:
stdout/stderr可能包含敏感输出,写入日志前按需截断或过滤;cmd是参数数组,注意拼接待执行命令时正确处理含空格参数; - 区分错误层级:连接、鉴权、GraphQL 协议类问题会以
GraphQLRequestError(D100)或UnknownDaggerError(D101)抛出,只有容器内命令真正失败才是ExecError(D109),据此可设计多级错误处理与重试策略。
ExecError的完整字段与类型签名始终以仓库中的 API 参考文档 ExecError.md、基类文档 DaggerSDKError.md 为准,结合 ExecError.ts 与 compute_query.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),仅供参考