409 表达的是状态冲突
409 Conflict 表示请求本身格式没问题,但与目标资源的当前状态冲突:并发编辑同一份文档、唯一键重复、状态机不允许的流转都属此类。它与 400(请求语法错误)和 412(条件请求不满足)的区别在于:409 的重点是服务端资源的实际状态,客户端需要先了解当前状态再决定下一步。
基本信息
| 名称 | 409 Conflict |
|---|---|
| 规范 | RFC 9110 §15.5.10 |
| 语义 | 请求与资源当前状态冲突,需先解决冲突再重试 |
| 响应体建议 | 给出冲突原因与当前状态,如冲突字段或当前版本号 |
| 相关码 | 412 前置条件失败、428 需要前置条件、423 资源被锁定 |
与相邻状态码的区别
| 状态码 | 触发条件 | 客户端应对 |
|---|---|---|
| 409 | 资源状态冲突(版本、唯一键、状态机) | 拉取最新状态,人工或自动合并后重发 |
| 412 | If-Match 等条件头不满足 | 重新获取 ETag 再带新值重试 |
| 428 | 服务器要求带条件头但请求没带 | 补上 If-Match 后重试 |
| 423 | 资源被锁定(WebDAV) | 等待锁释放或先解锁 |
版本冲突的交互
版本冲突的交互
curl -si -X PUT https://api.example.com/docs/7 \
-H 'If-Match: "v5"' -H 'Content-Type: text/markdown' -d @doc.md
# HTTP/1.1 409 Conflict(或 412 Precondition Failed,取决于实现)
# {"error":"version_conflict","current":"v6","conflict_fields":["title"]}
# 客户端应重新 GET 拿到最新内容与 ETag,再决定合并还是让用户处理典型使用场景
- 多人协作编辑同一资源时防止后写覆盖先写
- 注册或建单时唯一键重复,明确告知是哪个字段冲突
- 状态机拒绝非法流转:把「已发货订单再改地址」这类操作回成 409 而不是 500
- WebDAV 中父目录不存在造成的结构性冲突
WebDAV 里 409 也用于父目录缺失、资源类型不匹配等结构性冲突,排查时要先读响应体里的业务说明,再判断是并发还是结构问题。
把唯一键冲突映射成 409 而不是 500,能让前端把「用户名已被占用」呈现为表单提示,而不是「服务器错误,请稍后再试」。