Skip to content
Back to skills

Error Handling

ASecurity

设计或改进错误处理与日志时。

  • 4 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 6, 2026
ai-agentsapi

Works with

  • api

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned September 6, 2026

npx -y skills add Lion-1209/Lion-Skills --skill error-handling --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Error Handling?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Error Handling
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/lion-1209-error-handling/badge)](https://www.skillsdirectory.com/skills/lion-1209-error-handling)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
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.md4.9 KB
  • evals/evals.json1.2 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…