【OpenHarmony/HarmonyOs 】51|统一错误处理:从散落 catch 到可恢复的 ArkTS 错误体系
【OpenHarmony/HarmonyOs 】51|统一错误处理:从散落 catch 到可恢复的 ArkTS 错误体系 🧭
本文基于当前项目
CloudDBService.ets、LoginPage.ets、Index.ets、ChatPage.ets与ChatDetailPage.ets的真实异常处理展开。
项目已经使用try/catch/finally避免页面崩溃,也具备 Mock 降级与加载态,但错误仍以未知对象、原始字符串和页面级分支分散存在;本文给出统一错误模型、分层职责、用户提示与恢复策略。
一、为什么 catch 越多,错误处理不一定越好?
当前代码在登录、初始化、查询、订阅和发送消息处都写了 catch。
这能阻止异常直接击穿页面,却容易形成新的问题:
- 同一异常在多层重复打印;
- 页面直接展示 SDK message;
- 有的失败降级为 Mock,有的静默忽略;
- 调用方无法判断是否可重试;
finally虽结束 loading,却没有形成明确错误状态;- 未知对象被随意
JSON.stringify。
统一错误处理的目标不是减少 catch 数量,而是明确每一层应该负责什么。
二、项目当前的真实模式
Cloud DB 服务初始化失败后重新抛出:
try {
await AGConnectCloudDB.initialize(context);
} catch (err) {
hilog.error(0x0000, 'CloudDBService',
'Cloud DB Initialization failed: ' + JSON.stringify(err));
throw new Error(JSON.stringify(err));
}
登录页捕获后把错误字符串直接拼到界面:
catch (e) {
let errStr = JSON.stringify(e);
this.message = getStr(this.currentLang).login_failed + ': ' + errStr;
}
聊天列表失败时直接使用 Mock:
catch (e) {
this.chatList = this.getFallbackChatList();
} finally {
this.isLoading = false;
}
三种处理各有合理部分,但缺少共同语言。
三、统一错误模型应该回答什么?
一个可用的应用错误至少应回答:
- 错误属于哪个业务域;
- 稳定错误码是什么;
- 用户应该看到什么;
- 是否可以重试;
- 是否允许降级;
- 是否需要重新登录;
- 内部诊断追踪 ID 是什么;
- 原始异常是否保留在受控内存中。
四、推荐的错误类型
export enum AppErrorKind {
NETWORK = 'NETWORK',
AUTH = 'AUTH',
DATABASE = 'DATABASE',
VALIDATION = 'VALIDATION',
PERMISSION = 'PERMISSION',
NOT_FOUND = 'NOT_FOUND',
CONFLICT = 'CONFLICT',
CANCELLED = 'CANCELLED',
UNKNOWN = 'UNKNOWN'
}
export class AppError extends Error {
readonly code: string;
readonly kind: AppErrorKind;
readonly retryable: boolean;
readonly userMessageKey: string;
readonly traceId: string;
constructor(
code: string,
kind: AppErrorKind,
retryable: boolean,
userMessageKey: string,
traceId: string
) {
super(code);
this.code = code;
this.kind = kind;
this.retryable = retryable;
this.userMessageKey = userMessageKey;
this.traceId = traceId;
}
}
注意:示例强调结构,具体 API 和构造方式需要结合当前 ArkTS 编译规则验证。
五、错误码为什么必须稳定?
错误 message 可能变化:
- SDK 升级后英文文案变化;
- 系统语言变化;
- 云端返回字段变化;
- 开发者调整提示语。
错误码应保持稳定,例如:
AUTH_LOGIN_CANCELLED
AUTH_SESSION_EXPIRED
CLOUDDB_ZONE_UNAVAILABLE
CLOUDDB_QUERY_FAILED
CHAT_SEND_FAILED
PERMISSION_CAMERA_DENIED
VALIDATION_MESSAGE_EMPTY
页面根据 code 或 kind 决定行为,不依赖脆弱的 message 文本判断。
六、分层职责
SDK 适配层
把 AGC、Cloud DB 与系统 API 的异常转换为 AppError。
服务层
补充业务操作上下文,决定是否重试、是否降级,但不直接操作 UI。
ViewModel 或页面逻辑层
把错误映射为加载、空、错误、离线或需登录状态。
UI 层
展示本地化文案和恢复按钮,不解析 SDK 异常对象。
日志层
只记录安全错误码、阶段与追踪 ID,不复制用户数据。
七、错误标准化函数
推荐所有 catch 首先标准化:
function normalizeError(error: Object, operation: string): AppError {
if (error instanceof AppError) {
return error;
}
return new AppError(
operation + '_UNKNOWN',
AppErrorKind.UNKNOWN,
false,
'error_unknown',
createTraceId()
);
}
实际适配器应优先识别 SDK 明确错误码,未知情况才进入 UNKNOWN。
不要通过 message 包含某个英文单词来判断网络、权限或取消。
八、用户提示与诊断信息必须分离
用户需要的是:
- 发生了什么;
- 是否影响当前操作;
- 下一步可以做什么。
工程师需要的是:
- 错误码;
- 操作阶段;
- 追踪 ID;
- 耗时;
- 重试次数。
不能把后者原样拼接到前者。
推荐 UI 文案:
消息发送失败,请检查网络后重试。
推荐内部日志:
event=chat_send_failed code=CHAT_SEND_TIMEOUT trace=7F2A retry=1
九、页面状态不能只有 isLoading
建议状态至少包含:
enum LoadPhase {
IDLE = 'IDLE',
LOADING = 'LOADING',
CONTENT = 'CONTENT',
EMPTY = 'EMPTY',
ERROR = 'ERROR',
OFFLINE = 'OFFLINE'
}
这样页面可以区分:
- 首次加载失败;
- 有旧数据但刷新失败;
- Cloud DB 不可用且已降级;
- 真实空数据;
- 用户取消;
- 会话过期。
十、哪些错误适合重试?
通常可重试:
- 临时网络不可用;
- 超时;
- 服务暂时繁忙;
- 订阅中断;
- Zone 初始化竞态。
通常不可自动重试:
- 参数校验失败;
- 权限被用户拒绝;
- 登录被用户取消;
- 数据不存在;
- 业务冲突;
- 认证会话失效且需要用户操作。
重试策略必须由错误类型驱动,而不是任何 catch 都 setTimeout。
十一、逐项统一错误处理核验
检查项 1:AGC 初始化
- 检查目标:SDK 异常转换为稳定 AppError。
- 常见反例:直接 throw JSON 字符串。
- 推荐动作:映射初始化错误码与可重试性。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 2:Cloud DB Zone
- 检查目标:区分未初始化、打开失败和已关闭。
- 常见反例:统一当 unknown。
- 推荐动作:建立 ZONE_UNAVAILABLE 等明确代码。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 3:登录取消
- 检查目标:识别用户主动取消。
- 常见反例:显示“登录失败”并自动重试。
- 推荐动作:返回 CANCELLED 且不打 error。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 4:登录网络错误
- 检查目标:提示检查网络并允许重试。
- 常见反例:展示 SDK 原始 message。
- 推荐动作:映射 AUTH_NETWORK_ERROR。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 5:会话过期
- 检查目标:清理本地状态并回登录。
- 常见反例:无限重试原请求。
- 推荐动作:使用 AUTH_SESSION_EXPIRED。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 6:游客登录
- 检查目标:Cloud DB 失败可降级。
- 常见反例:初始化失败阻止游客进入。
- 推荐动作:按业务要求标记 fallback。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 7:用户同步
- 检查目标:同步失败不一定阻止登录。
- 常见反例:次要操作失败覆盖主流程成功。
- 推荐动作:区分关键与非关键副作用。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 8:用户查询
- 检查目标:首次失败进入错误态。
- 常见反例:无条件展示 Mock 且不提示。
- 推荐动作:标记数据来源与重试入口。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 9:聊天列表
- 检查目标:保留旧数据并提示刷新失败。
- 常见反例:catch 后清空内容。
- 推荐动作:区分 initial_error 与 refresh_error。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 10:消息订阅
- 检查目标:建立失败可手动重连。
- 常见反例:只 console.error 后静默。
- 推荐动作:维护 subscription state。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 11:订阅中断
- 检查目标:记录断线并退避重连。
- 常见反例:每次回调立即无限重订阅。
- 推荐动作:设置最大次数和抖动。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 12:发送消息
- 检查目标:发送失败保留本地失败气泡。
- 常见反例:catch 后消息消失。
- 推荐动作:使用 sending、sent、failed 状态。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 13:重复发送
- 检查目标:以客户端消息 ID 幂等。
- 常见反例:重试生成新 ID。
- 推荐动作:同一操作复用 idempotency key。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 14:消息为空
- 检查目标:本地校验并阻止请求。
- 常见反例:交给 Cloud DB 报错。
- 推荐动作:返回 VALIDATION_MESSAGE_EMPTY。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 15:附件失败
- 检查目标:按选择、读取、上传、发送分阶段。
- 常见反例:全部显示“操作失败”。
- 推荐动作:建立阶段化错误码。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 16:权限拒绝
- 检查目标:提示去设置或取消。
- 常见反例:当系统异常反复请求。
- 推荐动作:使用 PERMISSION_DENIED。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 17:路由参数
- 检查目标:入口校验类型与必填值。
- 常见反例:使用空 targetUid 继续查询。
- 推荐动作:返回 ROUTE_PARAM_INVALID。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 18:数据不存在
- 检查目标:显示空或已删除状态。
- 常见反例:当成系统错误。
- 推荐动作:使用 NOT_FOUND。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 19:并发冲突
- 检查目标:提示刷新后重试。
- 常见反例:静默覆盖新数据。
- 推荐动作:使用 CONFLICT 与版本控制。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 20:点赞失败
- 检查目标:回滚乐观更新。
- 常见反例:UI 数字永久错误。
- 推荐动作:保存旧值并按结果恢复。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 21:删除失败
- 检查目标:恢复被移除的本地项。
- 常见反例:先删 UI 后失败无提示。
- 推荐动作:使用事务式 UI 状态。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 22:心情发布
- 检查目标:防止重复点击。
- 常见反例:多次并发创建。
- 推荐动作:提交期锁定并使用唯一请求 ID。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 23:私密日记
- 检查目标:权限错误不能降级为公开。
- 常见反例:失败后自动改成公开保存。
- 推荐动作:安全边界错误禁止降级。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 24:搜索超时
- 检查目标:保留关键词并允许重试。
- 常见反例:清空输入。
- 推荐动作:错误状态与输入状态分离。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 25:搜索竞态
- 检查目标:旧错误不能覆盖新结果。
- 常见反例:任意 catch 设置 ERROR。
- 推荐动作:校验 requestVersion。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 26:分页失败
- 检查目标:保留已加载数据和游标。
- 常见反例:重置整个列表。
- 推荐动作:使用 load_more_error。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 27:无更多数据
- 检查目标:作为正常结束状态。
- 常见反例:记录 error。
- 推荐动作:使用 hasMore=false。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 28:空结果
- 检查目标:与失败状态明确区分。
- 常见反例:空数组统一显示失败。
- 推荐动作:建立 EMPTY phase。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 29:Mock 降级
- 检查目标:用户或测试可识别来源。
- 常见反例:伪装为云端成功。
- 推荐动作:使用 degraded=true。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 30:缓存回退
- 检查目标:展示缓存并提示可能过期。
- 常见反例:缓存命中掩盖请求失败。
- 推荐动作:记录 stale 状态。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 31:加载超时
- 检查目标:结束 loading 并提供恢复。
- 常见反例:转圈无限持续。
- 推荐动作:超时映射为 retryable error。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 32:finally
- 检查目标:只做无条件清理。
- 常见反例:finally 覆盖成功状态。
- 推荐动作:状态迁移在 try/catch 内完成。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 33:嵌套 try
- 检查目标:每层有明确责任。
- 常见反例:空 catch 吞掉初始化错误。
- 推荐动作:次要错误也应可观测。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 34:空 catch
- 检查目标:禁止无解释吞异常。
- 常见反例:catch (e) {}。
- 推荐动作:至少转换、降级或返回状态。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 35:重新抛出
- 检查目标:保留稳定错误类型。
- 常见反例:throw new Error(JSON.stringify(err))。
- 推荐动作:throw AppError 或标准化结果。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 36:未知类型
- 检查目标:安全处理 Object/unknown 来源。
- 常见反例:强制 any 后访问字段。
- 推荐动作:通过适配器和 instanceof 判断。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 37:Error message
- 检查目标:仅作内部安全摘要。
- 常见反例:把 message 当错误码。
- 推荐动作:code 与 message 分离。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 38:错误码
- 检查目标:全局唯一且稳定。
- 常见反例:页面自定义中文字符串。
- 推荐动作:域_操作_原因格式。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 39:错误类型
- 检查目标:覆盖网络、认证、数据库等域。
- 常见反例:所有错误都是 UNKNOWN。
- 推荐动作:优先使用 SDK code 映射。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 40:可重试性
- 检查目标:由类型决定。
- 常见反例:UI 自己猜测。
- 推荐动作:AppError.retryable 明确输出。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 41:降级性
- 检查目标:安全评估后决定。
- 常见反例:所有错误都回退 Mock。
- 推荐动作:权限与隐私错误禁止降级。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 42:严重级别
- 检查目标:区分提示、警告和阻断。
- 常见反例:任何失败都弹模态框。
- 推荐动作:按影响范围选择 UI。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 43:用户文案
- 检查目标:本地化且可行动。
- 常见反例:显示 JSON 异常。
- 推荐动作:使用 userMessageKey。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 44:诊断文案
- 检查目标:包含 code 与 trace。
- 常见反例:与用户提示混在一起。
- 推荐动作:只进入安全日志。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 45:追踪 ID
- 检查目标:每次操作稳定。
- 常见反例:使用 UID 作为 trace。
- 推荐动作:生成无身份含义的短 ID。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 46:日志去重
- 检查目标:责任边界只记一次。
- 常见反例:服务层和页面层重复 error。
- 推荐动作:下层转换,上层最终记录。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 47:Toast
- 检查目标:适合短暂且非阻断错误。
- 常见反例:重要错误一闪而过。
- 推荐动作:按严重度选择状态区或对话框。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 48:错误页
- 检查目标:适合首屏无法继续。
- 常见反例:局部按钮失败跳整页。
- 推荐动作:局部错误局部呈现。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 49:内联提示
- 检查目标:靠近输入或操作对象。
- 常见反例:所有校验都 Toast。
- 推荐动作:表单错误绑定字段。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 50:重试按钮
- 检查目标:只对 retryable 显示。
- 常见反例:权限拒绝也显示重试。
- 推荐动作:提供去设置等正确动作。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 51:取消按钮
- 检查目标:用户可退出长流程。
- 常见反例:强制等待不可取消请求。
- 推荐动作:取消映射为正常状态。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 52:防重复
- 检查目标:处理中禁用提交。
- 常见反例:按钮可连续触发。
- 推荐动作:isSubmitting 独立管理。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 53:请求版本
- 检查目标:只接受最新意图结果。
- 常见反例:旧失败覆盖新成功。
- 推荐动作:比较单调递增版本。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 54:页面销毁
- 检查目标:忽略或取消后续回调。
- 常见反例:离页后更新 @State。
- 推荐动作:维护 active 标识或取消句柄。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 55:超时控制
- 检查目标:每类操作有合理期限。
- 常见反例:所有请求共用一个超时。
- 推荐动作:登录、查询、上传分别配置。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 56:指数退避
- 检查目标:临时错误逐步延迟。
- 常见反例:固定 100ms 高频重试。
- 推荐动作:增加上限与随机抖动。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 57:最大重试
- 检查目标:存在明确上限。
- 常见反例:后台无限循环。
- 推荐动作:超限转手动恢复。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 58:网络恢复
- 检查目标:恢复时触发受控重试。
- 常见反例:每个页面同时请求。
- 推荐动作:集中协调恢复事件。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 59:离线写入
- 检查目标:明确队列和冲突策略。
- 常见反例:假装已同步成功。
- 推荐动作:展示 pending_sync。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 60:幂等设计
- 检查目标:写操作可安全重放。
- 常见反例:重试产生重复数据。
- 推荐动作:使用稳定业务 ID。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 61:事务边界
- 检查目标:关联写入共同成功或补偿。
- 常见反例:半成功无处理。
- 推荐动作:定义补偿动作。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 62:回滚失败
- 检查目标:升级错误并保留证据。
- 常见反例:继续假装已恢复。
- 推荐动作:使用 ROLLBACK_FAILED。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 63:数据解析
- 检查目标:校验字段完整与类型。
- 常见反例:JSON.parse 失败崩溃。
- 推荐动作:映射 DATA_INVALID。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 64:旧数据兼容
- 检查目标:未知枚举有回退。
- 常见反例:直接强转模型。
- 推荐动作:区分可修复与不可读。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 65:本地存储
- 检查目标:读写失败可解释。
- 常见反例:把 null、空串、损坏混为一类。
- 推荐动作:建立 STORAGE 错误域。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 66:资源读取
- 检查目标:缺失资源在构建或测试发现。
- 常见反例:运行时显示空文案。
- 推荐动作:错误键纳入校验。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 67:国际化键
- 检查目标:错误码映射有效 key。
- 常见反例:直接把 code 显示给用户。
- 推荐动作:缺失时使用通用安全文案。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 68:权限状态
- 检查目标:区分首次拒绝与永久拒绝。
- 常见反例:只显示 denied。
- 推荐动作:提供对应恢复路径。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 69:相机能力
- 检查目标:区分无设备、权限和调用失败。
- 常见反例:统一 UNKNOWN。
- 推荐动作:系统错误适配到业务域。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 70:相册选择
- 检查目标:用户取消不是 error。
- 常见反例:取消时弹失败。
- 推荐动作:使用 CANCELLED 正常结束。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 71:文件上传
- 检查目标:区分读取、网络和服务失败。
- 常见反例:统一“发送失败”。
- 推荐动作:阶段化状态支持针对性重试。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 72:大文件
- 检查目标:本地校验大小。
- 常见反例:上传后才报错。
- 推荐动作:使用 VALIDATION_FILE_TOO_LARGE。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 73:不支持格式
- 检查目标:在选择后立即提示。
- 常见反例:SDK 抛异常后显示内部信息。
- 推荐动作:建立格式校验码。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 74:数据库权限
- 检查目标:禁止自动改为公开查询。
- 常见反例:权限失败后全表查询。
- 推荐动作:安全错误必须阻断。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 75:数据库索引
- 检查目标:识别配置错误并阻断发布。
- 常见反例:当网络错误自动重试。
- 推荐动作:使用 DATABASE_INDEX_REQUIRED。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 76:对象模型
- 检查目标:Schema 不匹配有明确诊断。
- 常见反例:转换异常只显示 unknown。
- 推荐动作:记录模型版本和安全 code。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 77:订阅快照
- 检查目标:回调错误进入统一通道。
- 常见反例:只 console.error。
- 推荐动作:触发订阅状态变化。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 78:监听释放
- 检查目标:释放失败记录 warn。
- 常见反例:完全忽略。
- 推荐动作:保证生命周期最终一致。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 79:窗口加载
- 检查目标:加载失败保留错误码。
- 常见反例:完整序列化窗口异常。
- 推荐动作:能力层标准化。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 80:备份恢复
- 检查目标:区分版本不兼容与 IO 失败。
- 常见反例:一律成功日志。
- 推荐动作:失败向系统正确返回。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 81:启动守卫
- 检查目标:鉴权异常安全回登录。
- 常见反例:无限停留 loading。
- 推荐动作:设置最终路由状态。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 82:关键与非关键
- 检查目标:主流程与附加同步分开。
- 常见反例:头像同步失败导致登录失败。
- 推荐动作:定义 operation criticality。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 83:批量操作
- 检查目标:返回逐项结果或整体策略。
- 常见反例:首个失败后状态未知。
- 推荐动作:明确 all-or-nothing。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 84:部分成功
- 检查目标:UI 清楚展示成功与失败项。
- 常见反例:统一提示成功。
- 推荐动作:使用 PartialResult 模型。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 85:错误聚合
- 检查目标:多个并发失败保留主因。
- 常见反例:只显示最后一个错误。
- 推荐动作:按优先级选择主错误。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 86:错误优先级
- 检查目标:认证和权限高于普通网络提示。
- 常见反例:Toast 相互覆盖。
- 推荐动作:集中调度提示。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 87:全局处理器
- 检查目标:只兜底未处理异常。
- 常见反例:取代所有局部业务处理。
- 推荐动作:局部恢复优先,全局防崩溃。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 88:未捕获异常
- 检查目标:安全上报并终止危险流程。
- 常见反例:继续使用未知状态。
- 推荐动作:清理关键状态后提示重启。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 89:Promise 拒绝
- 检查目标:所有异步入口显式处理。
- 常见反例:调用 async 不 await。
- 推荐动作:返回 Promise 并在边界捕获。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 90:回调错误
- 检查目标:转换为同一错误模型。
- 常见反例:Object 类型随处传递。
- 推荐动作:在 SDK 回调边界适配。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 91:构建检查
- 检查目标:错误类型符合 ArkTS 规则。
- 常见反例:依赖 any 与对象字面量推断。
- 推荐动作:明确接口、类和返回类型。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 92:单元测试
- 检查目标:覆盖每个错误映射。
- 常见反例:只测成功路径。
- 推荐动作:使用表驱动错误码用例。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 93:恢复测试
- 检查目标:验证重试后状态闭合。
- 常见反例:只验证出现错误提示。
- 推荐动作:覆盖失败到成功迁移。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 94:弱网测试
- 检查目标:覆盖超时、断网和切网。
- 常见反例:只在稳定 Wi-Fi 测试。
- 推荐动作:注入延迟和失败。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 95:竞态测试
- 检查目标:旧成功和旧失败都被丢弃。
- 常见反例:只测响应顺序正常。
- 推荐动作:反转返回时序。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 96:生命周期测试
- 检查目标:离页后不更新 UI。
- 常见反例:只看无崩溃。
- 推荐动作:检查监听和任务释放。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 97:安全测试
- 检查目标:用户文案无内部信息。
- 常见反例:错误页显示堆栈。
- 推荐动作:扫描 code、URL、token 模式。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 98:可访问性
- 检查目标:错误被读屏感知。
- 常见反例:仅红色边框。
- 推荐动作:增加文本和状态语义。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 99:统计指标
- 检查目标:按稳定 code 聚合错误率。
- 常见反例:按 message 分组。
- 推荐动作:监控 code、operation、version。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 100:告警阈值
- 检查目标:关键错误有明确阈值。
- 常见反例:任何单次错误都告警。
- 推荐动作:结合比例、时间窗和影响用户数。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 101:灰度发布
- 检查目标:比较新旧错误分布。
- 常见反例:上线后才发现映射遗漏。
- 推荐动作:灰度观察 UNKNOWN 占比。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 102:UNKNOWN 治理
- 检查目标:持续降低未知错误比例。
- 常见反例:UNKNOWN 成为永久垃圾桶。
- 推荐动作:定期分析并补充适配。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 103:错误文档
- 检查目标:code 含义和恢复动作一致。
- 常见反例:文档与代码两套命名。
- 推荐动作:从代码枚举生成清单。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 104:代码评审
- 检查目标:检查 catch 是否履行职责。
- 常见反例:看到 catch 就认为安全。
- 推荐动作:审查转换、状态、提示、日志。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 105:发布检查
- 检查目标:P0/P1 错误路径已验证。
- 常见反例:只测 happy path。
- 推荐动作:按业务关键度建立矩阵。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
检查项 106:最终标准
- 检查目标:错误可识别、可解释、可恢复、可观测。
- 常见反例:catch 后不崩溃就算完成。
- 推荐动作:形成端到端闭环。
- 验证证据:错误码、页面状态、用户提示、日志事件与恢复结果。
- 通过标准:调用方无需解析 SDK 原始异常即可做出正确决策。
十二、推荐改造路线
阶段 1:盘点
检索所有 catch、throw、Promise 拒绝、回调错误和直接展示的异常字符串。
阶段 2:建模
建立 AppErrorKind、稳定 code、retryable、userMessageKey 与 traceId。
阶段 3:适配
先为 AGC Auth、Cloud DB、系统权限和文件选择器建立错误适配器。
阶段 4:页面状态
把 isLoading + message 升级为 IDLE、LOADING、CONTENT、EMPTY、ERROR、OFFLINE。
阶段 5:恢复
为可重试错误接入退避、手动重试、幂等和竞态隔离。
阶段 6:观测
日志只记录安全 code、operation 与 trace,并统计 UNKNOWN 比例。
阶段 7:测试
覆盖取消、权限、断网、超时、部分成功、离页回调与恢复成功。
十三、常见反模式速查
| 反模式 | 结果 | 正确方向 |
|---|---|---|
throw new Error(JSON.stringify(err)) |
类型与语义丢失 | 转换为稳定 AppError |
| UI 展示 SDK message | 内部信息泄露且不可本地化 | userMessageKey |
| 所有错误都自动重试 | 重复请求与糟糕体验 | retryable 驱动 |
| catch 后无状态 | 用户看见静默失败 | 明确 ERROR 或 DEGRADED |
| 空 catch | 故障不可观测 | 转换、降级或传播 |
| 多层重复 error 日志 | 噪声与重复告警 | 责任边界记录一次 |
| 取消也算失败 | 用户被错误责备 | CANCELLED 正常结束 |
| Mock 冒充云端成功 | 数据来源不透明 | degraded 状态 |
| 只用 isLoading | 空、错、离线混在一起 | 明确页面状态机 |
| 重试生成新业务 ID | 重复写入 | 幂等键 |
十四、总结
统一错误处理不是创建一个工具类后把所有 catch 搬进去。
真正完整的体系应贯穿:
- SDK 异常标准化;
- 服务层业务语义补充;
- 页面状态机;
- 本地化且可行动的用户提示;
- 安全日志和稳定错误指标;
- 重试、降级、回滚与幂等恢复。
当调用方只依赖稳定错误码和明确能力,而不再猜测 SDK message 时,错误才从“偶发异常”变成了可以设计、测试和治理的产品状态。

更多推荐



所有评论(0)