Back to skills
SKILL.md
Error Handling
ASecurity设计或改进错误处理与日志时。
- 4 stars
- 0 votes
- 0 copies
- 1 view
- Added September 6, 2026
Works with
Security analysis
100/100Pro scans all 2 files and shows the line behind each finding
npx -y skills add Lion-1209/Lion-Skills --skill error-handling --agent claude-codeAre you the author of Error Handling?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/lion-1209-error-handling)---
name: error-handling
description: 设计或改进错误处理与日志时。
---
# Error Handling
## 概述
错误要**可观测**(知道发生了什么)、**可区分**(能区分类型)、**不吞**(不静默 catch)。好的错误处理让你在出问题时能快速定位,而不是对着空白或 `undefined` 发呆。
## 何时使用
- 写代码遇到边界/失败场景(空值、超时、外部调用失败),不知怎么处理
- 现有错误处理粗糙(裸 catch、吞异常),要改进
- 设计错误类型/错误码体系
- 决定"这个错该抛还是该处理、要不要重试"
**不该用**:确定性逻辑(没失败可能,别过度防御)。
## 核心内容
### 三态决策:抛 / 处理 / 重试
遇到一个可能的错误,先判断属于哪种:
- **抛(throw / propagate)**:你处理不了,或属于底层职责。往上传,让上层决策。例:数据库连不上,service 层抛,让调用方决定降级还是报错。
- **处理(handle)**:你能**在原地给出可接受的后续**——三种形态:①**恢复**(用默认值/缓存顶上,如配置缺失用默认配置);②**降级**(核心功能照跑、非核心跳过,如推荐服务挂了就返回空列表而非整页崩溃);③**转成业务语义**(把底层异常翻译成业务错误类型抛出,让边界层转成给用户的提示)。**业务错误(余额不足、邮箱已注册)归这里**——它们不是故障而是预期分支,不重试(结果不会变),抛一个业务错误类型即可。判断"能不能处理"的尺子:**处理后程序能否继续负责任地往下走?**能 → 处理;只是把错藏起来让上层踩坑 → 抛。
- **重试(retry)**:只对**瞬时**故障重试——网络抖动、连接超时、HTTP 429/503。指数退避、有上限,耗尽后归为"抛"。三个前提:① 有副作用的调用(支付、下单、写库)重试前必须保证幂等(幂等键或唯一约束),否则重试=重复扣款/重复写入;② **读超时**(请求已发出、响应没回来)结果可能已生效,按副作用对待,必须幂等;③ HTTP 429/503 优先遵守响应头 `Retry-After`。注意 4xx(非 429)和多数 5xx 不是瞬时错误,重试无意义,直接抛。
**原则**:越靠近错误的层越了解错误含义;但只有能负责任的层才该处理。不知道怎么办就抛上去。
### 日志
- **记什么**:上下文——输入、相关状态、错误对象(含堆栈)
- **敏感信息要脱敏后记,不要完全不记**:密码、token、身份证、银行卡——直接丢弃会丢失排查线索("是不是这个用户的请求出问题?")。正确做法是记**可识别但不可还原**的形式:token 记前 8 位 + `***`、手机号记后 4 位、密码只记"是否为空"不记内容。原则:能定位到"是哪个对象/哪次请求",但不能还原出敏感值本身。
- **分级**:`debug`(排查细节)、`info`(关键业务节点)、`warn`(可疑但可继续)、`error`(出错了需关注)
**业务错误 vs 系统错误的日志级别**:邮箱已注册、余额不足这类是**预期的业务拒绝**,不算系统故障——打 `info`/`warn` 即可,别打 `error` 污染告警;DB 连不上、网络失败这类**意外故障**才打 `error`。
错误日志要让你光看日志就重现问题现场,而不是只看到 `Error: something went wrong`。
### 错误信息面向开发者
错误消息给**排查的开发者**看,不是给终端用户看:写清**发生了什么** + **可能原因** + **怎么排查**,并带上下文。
```
// 差
throw new Error("failed")
// 好
throw new Error(`用户注册失败:邮箱 ${email} 已被注册。检查是否应走登录流程。`)
```
给用户的提示要另外做(友好、不泄露技术细节),别直接把异常 message 吐给用户。
### 模式
- **自定义错误类型/错误码**:区分业务错误(邮箱已注册)和系统错误(DB 连不上),让上层针对性处理
- **fail fast**:启动期检查前置条件,不满足直接报错,别带病运行
- **边界统一兜底**:在系统边界(HTTP 中间件、API 网关)统一捕获并转换错误,别让内部异常直接漏到外部
- **别 catch 了又原样 throw**:要么加上下文再抛,要么处理掉
## 常见错误
| 问题 | 修法 |
|------|------|
| 裸 `catch(e){}` 静默吞错 | 至少记日志 + 决定抛/处理 |
| catch 了又 `throw e`(无添加) | 加上下文再抛,或处理掉 |
| 日志只记 `e.message` | 记上下文 + 完整错误对象 |
| 敏感信息要么全记要么全丢 | 脱敏后记(token 记前 8 位 + `***`),保留定位能力又不泄露 |
| 把堆栈直接吐给终端用户 | 内部日志详细,给用户友好提示 |
| 不区分错误类型 | 用自定义错误类型/错误码 |
| 无限重试/无退避 | 指数退避 + 最大次数 |
Files in this skill
- SKILL.md
- evals/evals.json
Attribution
Comments
Loading comments…