跳转到内容

输入关键词开始搜索文档。

错误

最后更新 查看 Markdown 源文件

Flue Agent SDK(@flue/sdk)导出两个自有的错误类 — FlueApiError(失败的 HTTP 请求)和 FlueExecutionError(已接收但结算为失败或中止的提交)— 以及四个由 @durable-streams/client 拥有的重新导出的流错误类。这些错误携带的线路格式(HTTP 错误信封和序列化的结算错误)记录在本页。

每个类将 name 设置为类名。使用 instanceof 进行区分;消息字符串是为日志记录而组合的,不是 API。

import { createFlueClient, FlueApiError, FlueExecutionError } from '@flue/sdk';

const conversation = createFlueClient({ url: 'https://example.com/agents/triage/123456' });

try {
  const admission = await conversation.send({ message: { kind: 'user', body: 'Hello' } });
  await conversation.wait(admission);
} catch (error) {
  if (error instanceof FlueApiError) {
    // HTTP 请求被拒绝:error.status、error.body。
  } else if (error instanceof FlueExecutionError) {
    // 消息已被接收,但提交结算为失败或中止。
  } else {
    throw error;
  }
}

FlueApiError

class FlueApiError extends Error {
  readonly status: number;
  readonly body: unknown;
  readonly ref: string | undefined;
  constructor(status: number, body: unknown, headerRef?: string);
}

SDK 中每个返回非 2xx 响应的 JSON 请求(send()、abort() 和 history())的拒绝值。SDK 对每个 JSON 请求只执行一次 fetch — 无重试 — 因此 FlueApiError 反映的是单个服务器响应。

字段描述
statusHTTP 响应状态码。
body解析成功时的 JSON 响应体;解析失败时的原始响应文本;响应无 body 时的空字符串。故意为 unknown:部署前的代理和网关可以返回任意 body,因此 SDK 不做规范化。
ref服务器的错误关联引用(err_…),在运行时在服务端记录了失败时存在(500 级内部错误)。从信封的 error.ref 读取,以 flue-error-ref 响应头部作为回退。报告生产环境 500 错误时引用它 — 它命名了精确的服务端日志行(参见关联生产环境 500)。

message 由状态码、body 携带 Flue 错误信封时的 type 和 message、以及响应携带时的 ref 组合而成 — 例如 Flue API error 404 [agent_instance_not_found]: Agent instance "123456" was not found. 或 Flue API error 500 [internal_error] (ref err_…): An internal error occurred. — 因此记录被捕获的 FlueApiError 会将 ref 带入你自己的日志而无需额外代码。按 status 和 body 匹配,而非按字符串。

两个方法永远不会产生 FlueApiError:

  • wait() 读取持久流而非发起 JSON 请求;其传输失败是下面的流错误。
  • observe() 根本不会抛出;其内部历史读取的 FlueApiError 会落到观察快照上(参见 observe() 中的错误)。

HTTP 错误信封

Flue 运行时自身渲染的每个错误响应都携带一个 JSON 信封 — { "error": ... },格式如下:

{
  type: string;
  message: string;
  details: string;
  dev?: string;
  meta?: Record<string, unknown>;
}
字段描述
type稳定的、机器可读的标识符(snake_case,如 agent_instance_not_found)。这是分支判断的字段;message 和 details 的措辞不是 API。
message一句话摘要,可安全展示给任何调用方。
details更长的调用方安全说明。始终存在(可能为空)。
dev面向开发者的指导(修复说明、配置提示)。仅在服务器以本地开发模式运行且错误有仅限开发者的指导时存在;其缺失不是可靠的生产信号。
meta可选的结构化数据,仅在下游工具受益时设置。

FlueApiError.body 持有整个信封;读取字段前请检查格式(body 保持 unknown,因为非 Flue 基础设施也可能响应)。服务端错误词汇 — 哪些 type 值存在及其状态 — 记录在运行时错误参考中。两个与 send() 的 uid 条件相关的拒绝值得在此了解:

  • 404 agent_instance_not_found — 带 uid 条件的发送命名了一个不存在或 uid 不再匹配的实例(实例被重新创建,uid 命名的是其之前的化身);未投递任何内容。这两种情况故意无法区分。
  • 409 agent_instance_exists — 仅创建的发送(uid: null)命名了一个已有实例;meta.uid 返回已有的 uid(在 details 正文中重复),因此调用方可以继续该化身而无需单独查找。

_应用_中间件或非 Flue 基础设施抛出的错误不会被信封化;这就是为什么 body 保持 unknown。

FlueExecutionError

type FlueExecutionTarget = 'agent_submission';
type FlueExecutionFailure = 'failed' | 'aborted' | 'terminal_event_missing';

class FlueExecutionError extends Error {
  readonly target: FlueExecutionTarget;
  readonly targetId: string;
  readonly failure: FlueExecutionFailure;
  readonly error: unknown;
  constructor(options: {
    target: FlueExecutionTarget;
    targetId: string;
    failure: FlueExecutionFailure;
    error?: unknown;
  });
}

wait() 的拒绝值:消息已被接收并执行,但提交结算的结果不是 completed。FlueApiError 意味着请求从未进入;FlueExecutionError 意味着智能体运行了但未成功完成。

字段描述
target被等待的内容。该联合类型目前只有一个成员 'agent_submission'。
targetId被等待提交的 submissionId(来自 AgentSendResult 接收结果)。
failure提交结算为失败时为 'failed';结算为中止时为 'aborted'(例如 abort() 之后);会话流结束但没有此提交的终止事件时为 'terminal_event_missing'。
error结算的错误负载(当结算携带时);terminal_event_missing 时为 undefined。类型为 unknown,因为该值跨线路传输,但运行时自身序列化的错误遵循此格式:
{
  name?: string;
  message: string;
  type?: string;
  details?: string;
  dev?: string;
  meta?: Record<string, unknown>;
}

当下层失败是类型化的运行时错误(与运行时错误参考相同的标识词汇)时,type、details 和 meta 存在;普通错误仅序列化为 name 和 message。

wait() 区分服务端中止和调用方取消:通过 abort() 中止的会话以 FlueExecutionError(failure: 'aborted')拒绝,而触发调用方自身的 AbortSignal 以信号的原因拒绝 — 当信号没有原因时是名为 AbortError 的 DOMException — 且不是 FlueExecutionError。参见中止不是 SDK 错误。

会话线路(wait()、observe())根据物化会话协议验证每个块,协议不匹配会引发内部的 ConversationStreamError(name: 'ConversationStreamError',未导出)。observe() 通过重填全新快照来恢复;在 wait() 中它作为拒绝传播。

流错误

wait()、observe() 和 FlueEventStream 迭代读取持久流。这些读取上的传输和协议失败来自 @durable-streams/client;SDK 重新导出了通过其读取路径可达的类,以便应用无需直接依赖该包即可区分它们。它们的格式由该包拥有并跟踪其发布。

DurableStreamError

class DurableStreamError extends Error {
  code:
    | 'NOT_FOUND'
    | 'CONFLICT_SEQ'
    | 'CONFLICT_EXISTS'
    | 'BAD_REQUEST'
    | 'BUSY'
    | 'SSE_NOT_SUPPORTED'
    | 'UNAUTHORIZED'
    | 'FORBIDDEN'
    | 'RATE_LIMITED'
    | 'ALREADY_CONSUMED'
    | 'ALREADY_CLOSED'
    | 'PARSE_ERROR'
    | 'STREAM_CLOSED'
    | 'UNKNOWN';
  status?: number;
  details?: unknown;
}

协议级流失败 — 格式错误或无法解析的流响应、服务器不支持的 SSE 升级,或映射到结构化代码的 HTTP 状态。

字段描述
code用于程序化处理的结构化代码(上面的联合类型;DurableStreamErrorCode 类型别名本身未重新导出)。
statusHTTP 状态(当失败映射到响应时)。
details附加数据,通常是原始响应体。

StreamClosedError

class StreamClosedError extends DurableStreamError {
  readonly code = 'STREAM_CLOSED';
  readonly status = 409;
  readonly streamClosed = true;
  readonly finalOffset?: string;
}

DurableStreamError 子类,报告对已关闭流的操作。当响应提供了最终偏移量时,finalOffset 携带流的最终偏移量。

FetchError

class FetchError extends Error {
  status: number;
  text?: string;
  json?: object;
  headers: Record<string, string>;
  url: string;
}

没有协议级分类的流 HTTP 请求失败。流层以指数退避重试瞬态失败(429、503、所有 5xx 和网络错误;默认无限制 — 通过 wait() 和 observe() 接受的 backoffOptions 来限制),因此使用默认选项时,FetchError 出现在不可重试的客户端错误上:除 429 外的 4xx。

字段描述
status失败请求的 HTTP 状态。
text / json响应体,根据响应内容类型为文本或解析后的 JSON。
headers响应头部。
url请求的 URL。

FetchBackoffAbortError

class FetchBackoffAbortError extends Error {}

流请求因信号在重试退避期间中止而被放弃。没有额外字段。大多数调用方发起的中止不会暴露此类 — SDK 流迭代在调用方自身的信号上安静结束,wait() 然后以信号的原因拒绝 — 但它可能在流层的中止与重试循环竞争时出现。

observe() 中的错误

observe() 既不抛出也不拒绝。失败在观察快照(getSnapshot().phase 和 .error)上暴露:

  • 初始历史读取返回 404 时设置 phase: 'absent' 且无错误 — 会话尚不存在。
  • 400、401 或 403(来自任何携带数字 status 的错误值,如 FlueApiError 或 FetchError)是致命的:phase: 'error',错误在 snapshot.error 上。无进一步重试。
  • 所有其他失败 — 网络错误、5xx、意外结束的流 — 以指数延迟(1 秒翻倍,上限 30 秒)安排重填:phase: 'connecting',待处理的错误在 snapshot.error 上。
  • 关闭(通过 close() 或 signal 选项)设置 phase: 'closed'。

完整的观察契约在客户端页面上。

中止不是 SDK 错误

取消表现为中止原因,而不是 Flue 错误类:

  • send()、abort() 和 history() 以 fetch 实现为中止请求抛出的任何内容拒绝 — 标准 fetch 下是名为 AbortError 的 DOMException。
  • wait() 以 signal.reason 拒绝,当信号没有原因时是名为 AbortError 的 DOMException。
  • FlueEventStream 迭代在取消时不抛出:cancel()(或跳出 for await)以 done: true 结束迭代。

在将拒绝视为失败之前,检查 error.name === 'AbortError'(或你自己的 signal.reason)。

构造错误

createFlueClient() 在无法解析会话 URL 时同步抛出 — 使用原生错误而非 SDK 类:

  • 在浏览器外使用相对 url 抛出 TypeError: relative url requires a browser; pass an absolute URL。在浏览器中,相对 URL 相对于 location.origin 解析。
  • 不是有效 URL 的 url 抛出来自 URL 构造函数的原生 TypeError。

构造时不会发生网络活动;其他所有失败在调用时通过上述类抛出。