<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Error-Model on Tarragon</title><link>https://tarrragon.github.io/blog/tags/error-model/</link><description>Recent content in Error-Model on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Fri, 03 Jul 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/error-model/index.xml" rel="self" type="application/rss+xml"/><item><title>11.4 錯誤模型設計</title><link>https://tarrragon.github.io/blog/backend/11-api-design/error-model-design/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/error-model-design/</guid><description>&lt;p>錯誤模型是契約的一級公民：消費者的重試邏輯、監控的告警規則、前端的使用者訊息、全部建立在錯誤回應的結構上。錯誤格式一旦被依賴、變更成本跟正常回應完全相同（承諾成本結構見 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/api-boundary-responsibility/" data-link-title="11.1 API 作為服務邊界的責任" data-link-desc="介面變更該由誰付成本、哪些介面性質算對外承諾、承諾違約有哪些模式 — 動手設計 endpoint 前的責任框架">11.1&lt;/a>）、常見的失衡是設計精力集中在成功路徑、錯誤格式在第一個 handler 裡即興決定 — 之後每個新錯誤都在複製那次即興。&lt;/p>
&lt;h2 id="第一刀可重試與終態">第一刀：可重試與終態&lt;/h2>
&lt;p>錯誤分類的第一個維度是對消費者行為的指示：這個錯誤重試有沒有用。可重試（服務暫時失效、限流、鎖衝突）指示消費者退避後重送；終態（參數錯誤、權限拒絕、業務規則拒絕）指示消費者停止重試、走修正或人工路徑。這一刀切錯的代價是雙向的：終態錯誤被標成可重試、消費者的 retry 迴圈空轉壓垮服務；可重試被標成終態、暫時性故障變成使用者眼中的永久失敗。&lt;/p>
&lt;p>HTTP status 承擔這一刀的粗分類（4xx 終態、5xx 與 429 可重試、見 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/resource-modeling-operation-semantics/" data-link-title="11.3 資源建模與操作語意" data-link-desc="endpoint 該建模成資源還是動作、HTTP method 與 status 承諾了什麼、available actions 由誰計算 — 建模決策的判準">11.3&lt;/a> 的 status 承諾段）、錯誤 body 承擔細分類。兩層要一致 — body 說可重試、status 給 400、中介層跟 SDK 只看 status、消費者的兩層邏輯就互相矛盾。&lt;/p>
&lt;h2 id="格式設計標準與自訂並存的現實">格式設計：標準與自訂並存的現實&lt;/h2>
&lt;p>錯誤格式有現行標準、也有大廠自成一格的成熟先例、兩者的設計目標一致：機器可判讀、人類可理解、格式可演化。&lt;/p>
&lt;p>RFC 9457 定義 &lt;code>application/problem+json&lt;/code>：&lt;code>type&lt;/code>（URI）、&lt;code>title&lt;/code>、&lt;code>status&lt;/code>、&lt;code>detail&lt;/code>、&lt;code>instance&lt;/code> 五個核心成員、允許 extension members 且要求 client 忽略不認識的欄位（見 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/error-rfc9457-problem-details/" data-link-title="11.C35 RFC 9457：problem&amp;#43;json 標準化錯誤格式" data-link-desc="type 用 URI 外部化錯誤命名空間、client 必須忽略未知欄位的演化條款、IANA registry 補 7807 碎片化">11.C35&lt;/a>）。兩個設計值得單獨理解：&lt;code>type&lt;/code> 用 URI 而非字串 enum、把錯誤種類的命名空間外部化、跨團隊不撞名；「client MUST ignore unknown extensions」是格式的演化條款 — 服務端可以加欄位而不破壞既有消費者、等同錯誤模型的開放封閉原則。&lt;/p>
&lt;p>Stripe 的錯誤物件早於這個標準自成一格、分層思路可以直接借用：&lt;code>type&lt;/code> 承擔路由層（哪類錯誤、走哪條處理分支）、&lt;code>code&lt;/code> 承擔分支層（細粒度機器碼）、&lt;code>param&lt;/code> 加 &lt;code>message&lt;/code> 承擔 UI 層（哪個欄位錯、給人看什麼）、三個正交欄位讓消費者各層各取所需（見 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/error-stripe-error-object/" data-link-title="11.C36 Stripe 錯誤物件：type / code / param 三層分離" data-link-desc="路由層、分支層、UI 層做成正交欄位；冪等衝突列 first-class 錯誤型別；標準前自成一格的對照組">11.C36&lt;/a>）。這個模型還藏著一個結構訊號：&lt;code>idempotency_error&lt;/code> 是四個 type 之一 — 冪等衝突在支付 API 是預期常態、錯誤模型要為它保留一級位置（冪等語意主寫在 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/api-idempotency-design/" data-link-title="11.8 API 層冪等設計" data-link-desc="idempotency key 誰生成、存多久、replay 回什麼、衝突怎麼回 — 對外冪等契約的條款設計與無標準現況">11.8&lt;/a>）。&lt;/p>
&lt;p>選標準還是自訂的判準：新 API 從 RFC 9457 起手、拿到現成的演化條款與工具生態；既有 API 有自訂格式且被大量依賴、遷移本身就是 breaking change、務實做法是把 9457 的兩個設計（type 命名空間化、未知欄位忽略條款）補進自訂格式、而非換格式。&lt;/p>
&lt;h2 id="錯誤狀態下的系統行為">錯誤狀態下的系統行為&lt;/h2>
&lt;p>錯誤模型的最後一段責任是「錯誤發生時、系統還敢做什麼」。Twilio 2013 年計費事故的教訓落在這：關鍵狀態讀不到、自動扣款卻繼續跑、演變成重複扣款（事故時序與冪等閘門的抽象、主寫在 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/api-idempotency-design/" data-link-title="11.8 API 層冪等設計" data-link-desc="idempotency key 誰生成、存多久、replay 回什麼、衝突怎麼回 — 對外冪等契約的條款設計與無標準現況">11.8 的反例段&lt;/a>）。落到錯誤模型的通用判準：關鍵狀態讀寫失敗的錯誤處理、預設要往「拒絕服務」收斂、而非「帶著壞狀態繼續」— 錯誤分類表裡要有一類「狀態不可信、停止副作用」、它的處理路徑跟一般 5xx 不同。&lt;/p>
&lt;h2 id="常見設計錯誤">常見設計錯誤&lt;/h2>
&lt;ul>
&lt;li>&lt;strong>業務失敗包 200&lt;/strong>：觀測與重試鏈失真、修法見 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/resource-modeling-operation-semantics/" data-link-title="11.3 資源建模與操作語意" data-link-desc="endpoint 該建模成資源還是動作、HTTP method 與 status 承諾了什麼、available actions 由誰計算 — 建模決策的判準">11.3 的 status 承諾段&lt;/a>。&lt;/li>
&lt;li>&lt;strong>錯誤碼用連續數字&lt;/strong>：&lt;code>code: 1047&lt;/code> 無命名空間、跨服務撞號、grep 不到語意 — 用可讀字串或 URI。&lt;/li>
&lt;li>&lt;strong>message 當機器介面&lt;/strong>：消費者 parse 錯誤訊息文字做分支、訊息改字就是 breaking change — 機器分支一律走 type / code。&lt;/li>
&lt;li>&lt;strong>錯誤格式沒有演化條款&lt;/strong>：第一版沒宣告「未知欄位忽略」、之後每次加欄位都無法確認安全性 — 條款從第一版就寫進文件。&lt;/li>
&lt;/ul>
&lt;h2 id="爭論地圖與下一步">爭論地圖與下一步&lt;/h2>
&lt;p>本章的分類與格式判準、以 HTTP transport 承載 status 語意為前提。錯誤格式的跨風格交鋒（RFC 9457、envelope 包裝、GraphQL 的 200-with-errors 慣例）是掛在本章的爭論深度文章 backlog（見 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/" data-link-title="模組十一：API 設計與對外契約" data-link-desc="整理 API 風格選型、資源建模、錯誤模型、版本與相容策略、冪等與對外流量語意的設計判準；主流做法與各流派的深度論證分層收錄">模組頁&lt;/a> 章節規劃）— GraphQL 把 transport 層 status 跟業務錯誤解耦的做法、在該文攤開、本章不展開。&lt;/p></description><content:encoded><![CDATA[<p>錯誤模型是契約的一級公民：消費者的重試邏輯、監控的告警規則、前端的使用者訊息、全部建立在錯誤回應的結構上。錯誤格式一旦被依賴、變更成本跟正常回應完全相同（承諾成本結構見 <a href="/blog/backend/11-api-design/api-boundary-responsibility/" data-link-title="11.1 API 作為服務邊界的責任" data-link-desc="介面變更該由誰付成本、哪些介面性質算對外承諾、承諾違約有哪些模式 — 動手設計 endpoint 前的責任框架">11.1</a>）、常見的失衡是設計精力集中在成功路徑、錯誤格式在第一個 handler 裡即興決定 — 之後每個新錯誤都在複製那次即興。</p>
<h2 id="第一刀可重試與終態">第一刀：可重試與終態</h2>
<p>錯誤分類的第一個維度是對消費者行為的指示：這個錯誤重試有沒有用。可重試（服務暫時失效、限流、鎖衝突）指示消費者退避後重送；終態（參數錯誤、權限拒絕、業務規則拒絕）指示消費者停止重試、走修正或人工路徑。這一刀切錯的代價是雙向的：終態錯誤被標成可重試、消費者的 retry 迴圈空轉壓垮服務；可重試被標成終態、暫時性故障變成使用者眼中的永久失敗。</p>
<p>HTTP status 承擔這一刀的粗分類（4xx 終態、5xx 與 429 可重試、見 <a href="/blog/backend/11-api-design/resource-modeling-operation-semantics/" data-link-title="11.3 資源建模與操作語意" data-link-desc="endpoint 該建模成資源還是動作、HTTP method 與 status 承諾了什麼、available actions 由誰計算 — 建模決策的判準">11.3</a> 的 status 承諾段）、錯誤 body 承擔細分類。兩層要一致 — body 說可重試、status 給 400、中介層跟 SDK 只看 status、消費者的兩層邏輯就互相矛盾。</p>
<h2 id="格式設計標準與自訂並存的現實">格式設計：標準與自訂並存的現實</h2>
<p>錯誤格式有現行標準、也有大廠自成一格的成熟先例、兩者的設計目標一致：機器可判讀、人類可理解、格式可演化。</p>
<p>RFC 9457 定義 <code>application/problem+json</code>：<code>type</code>（URI）、<code>title</code>、<code>status</code>、<code>detail</code>、<code>instance</code> 五個核心成員、允許 extension members 且要求 client 忽略不認識的欄位（見 <a href="/blog/backend/11-api-design/cases/error-rfc9457-problem-details/" data-link-title="11.C35 RFC 9457：problem&#43;json 標準化錯誤格式" data-link-desc="type 用 URI 外部化錯誤命名空間、client 必須忽略未知欄位的演化條款、IANA registry 補 7807 碎片化">11.C35</a>）。兩個設計值得單獨理解：<code>type</code> 用 URI 而非字串 enum、把錯誤種類的命名空間外部化、跨團隊不撞名；「client MUST ignore unknown extensions」是格式的演化條款 — 服務端可以加欄位而不破壞既有消費者、等同錯誤模型的開放封閉原則。</p>
<p>Stripe 的錯誤物件早於這個標準自成一格、分層思路可以直接借用：<code>type</code> 承擔路由層（哪類錯誤、走哪條處理分支）、<code>code</code> 承擔分支層（細粒度機器碼）、<code>param</code> 加 <code>message</code> 承擔 UI 層（哪個欄位錯、給人看什麼）、三個正交欄位讓消費者各層各取所需（見 <a href="/blog/backend/11-api-design/cases/error-stripe-error-object/" data-link-title="11.C36 Stripe 錯誤物件：type / code / param 三層分離" data-link-desc="路由層、分支層、UI 層做成正交欄位；冪等衝突列 first-class 錯誤型別；標準前自成一格的對照組">11.C36</a>）。這個模型還藏著一個結構訊號：<code>idempotency_error</code> 是四個 type 之一 — 冪等衝突在支付 API 是預期常態、錯誤模型要為它保留一級位置（冪等語意主寫在 <a href="/blog/backend/11-api-design/api-idempotency-design/" data-link-title="11.8 API 層冪等設計" data-link-desc="idempotency key 誰生成、存多久、replay 回什麼、衝突怎麼回 — 對外冪等契約的條款設計與無標準現況">11.8</a>）。</p>
<p>選標準還是自訂的判準：新 API 從 RFC 9457 起手、拿到現成的演化條款與工具生態；既有 API 有自訂格式且被大量依賴、遷移本身就是 breaking change、務實做法是把 9457 的兩個設計（type 命名空間化、未知欄位忽略條款）補進自訂格式、而非換格式。</p>
<h2 id="錯誤狀態下的系統行為">錯誤狀態下的系統行為</h2>
<p>錯誤模型的最後一段責任是「錯誤發生時、系統還敢做什麼」。Twilio 2013 年計費事故的教訓落在這：關鍵狀態讀不到、自動扣款卻繼續跑、演變成重複扣款（事故時序與冪等閘門的抽象、主寫在 <a href="/blog/backend/11-api-design/api-idempotency-design/" data-link-title="11.8 API 層冪等設計" data-link-desc="idempotency key 誰生成、存多久、replay 回什麼、衝突怎麼回 — 對外冪等契約的條款設計與無標準現況">11.8 的反例段</a>）。落到錯誤模型的通用判準：關鍵狀態讀寫失敗的錯誤處理、預設要往「拒絕服務」收斂、而非「帶著壞狀態繼續」— 錯誤分類表裡要有一類「狀態不可信、停止副作用」、它的處理路徑跟一般 5xx 不同。</p>
<h2 id="常見設計錯誤">常見設計錯誤</h2>
<ul>
<li><strong>業務失敗包 200</strong>：觀測與重試鏈失真、修法見 <a href="/blog/backend/11-api-design/resource-modeling-operation-semantics/" data-link-title="11.3 資源建模與操作語意" data-link-desc="endpoint 該建模成資源還是動作、HTTP method 與 status 承諾了什麼、available actions 由誰計算 — 建模決策的判準">11.3 的 status 承諾段</a>。</li>
<li><strong>錯誤碼用連續數字</strong>：<code>code: 1047</code> 無命名空間、跨服務撞號、grep 不到語意 — 用可讀字串或 URI。</li>
<li><strong>message 當機器介面</strong>：消費者 parse 錯誤訊息文字做分支、訊息改字就是 breaking change — 機器分支一律走 type / code。</li>
<li><strong>錯誤格式沒有演化條款</strong>：第一版沒宣告「未知欄位忽略」、之後每次加欄位都無法確認安全性 — 條款從第一版就寫進文件。</li>
</ul>
<h2 id="爭論地圖與下一步">爭論地圖與下一步</h2>
<p>本章的分類與格式判準、以 HTTP transport 承載 status 語意為前提。錯誤格式的跨風格交鋒（RFC 9457、envelope 包裝、GraphQL 的 200-with-errors 慣例）是掛在本章的爭論深度文章 backlog（見 <a href="/blog/backend/11-api-design/" data-link-title="模組十一：API 設計與對外契約" data-link-desc="整理 API 風格選型、資源建模、錯誤模型、版本與相容策略、冪等與對外流量語意的設計判準；主流做法與各流派的深度論證分層收錄">模組頁</a> 章節規劃）— GraphQL 把 transport 層 status 跟業務錯誤解耦的做法、在該文攤開、本章不展開。</p>
<ul>
<li>可重試錯誤的消費端行為設計：<a href="/blog/backend/11-api-design/api-idempotency-design/" data-link-title="11.8 API 層冪等設計" data-link-desc="idempotency key 誰生成、存多久、replay 回什麼、衝突怎麼回 — 對外冪等契約的條款設計與無標準現況">11.8 API 層冪等設計</a>、<a href="/blog/backend/06-reliability/idempotency-replay/" data-link-title="6.12 Idempotency 與 Replay 驗證" data-link-desc="把重試、重播與冪等性從口頭約定變成可驗證屬性">6.12 冪等與重放驗證</a></li>
<li>限流錯誤（429）的完整語意：<a href="/blog/backend/11-api-design/external-traffic-semantics/" data-link-title="11.9 對外流量語意" data-link-desc="rate limit 對消費者承諾什麼、429 與 Retry-After 怎麼設計、配額 header 該不該信 — 限流作為契約的語意設計">11.9 對外流量語意</a></li>
<li>案例原文：<a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a></li>
</ul>
]]></content:encoded></item><item><title>11.C35 RFC 9457：problem+json 標準化錯誤格式</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/error-rfc9457-problem-details/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/error-rfc9457-problem-details/</guid><description>&lt;p>這個案例的核心責任是提供錯誤格式標準化的現行基準（obsoletes RFC 7807）。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>RFC 9457（IETF Proposed Standard、Standards Track、2023-07）定義 &lt;code>application/problem+json&lt;/code>：五個核心成員 &lt;code>type&lt;/code>（URI、預設 &lt;code>about:blank&lt;/code>）、&lt;code>title&lt;/code>、&lt;code>status&lt;/code>、&lt;code>detail&lt;/code>、&lt;code>instance&lt;/code>；允許 extension members 且要求 client 忽略不認識的欄位；建立 IANA common problem types registry（7807 沒有）。RFC 明言 problem details 是補充 HTTP status code、不是取代。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>&lt;code>type&lt;/code> 用 URI 而非字串 enum、把錯誤種類的命名空間外部化、避免各團隊 code 撞名；「client MUST ignore unknown extensions」是向前相容的演化條款、等同錯誤模型的 OCP。registry 的新增是對 7807 生態碎片化的直接回應。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>11.4 錯誤模型設計（anchor）、錯誤格式爭論文章。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://www.rfc-editor.org/rfc/rfc9457.html">RFC 9457: Problem Details for HTTP APIs（IETF、2023）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是提供錯誤格式標準化的現行基準（obsoletes RFC 7807）。</p>
<h2 id="觀察">觀察</h2>
<p>RFC 9457（IETF Proposed Standard、Standards Track、2023-07）定義 <code>application/problem+json</code>：五個核心成員 <code>type</code>（URI、預設 <code>about:blank</code>）、<code>title</code>、<code>status</code>、<code>detail</code>、<code>instance</code>；允許 extension members 且要求 client 忽略不認識的欄位；建立 IANA common problem types registry（7807 沒有）。RFC 明言 problem details 是補充 HTTP status code、不是取代。</p>
<h2 id="判讀">判讀</h2>
<p><code>type</code> 用 URI 而非字串 enum、把錯誤種類的命名空間外部化、避免各團隊 code 撞名；「client MUST ignore unknown extensions」是向前相容的演化條款、等同錯誤模型的 OCP。registry 的新增是對 7807 生態碎片化的直接回應。</p>
<h2 id="對應大綱">對應大綱</h2>
<p>11.4 錯誤模型設計（anchor）、錯誤格式爭論文章。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://www.rfc-editor.org/rfc/rfc9457.html">RFC 9457: Problem Details for HTTP APIs（IETF、2023）</a></li>
</ul>
]]></content:encoded></item><item><title>11.C36 Stripe 錯誤物件：type / code / param 三層分離</title><link>https://tarrragon.github.io/blog/backend/11-api-design/cases/error-stripe-error-object/</link><pubDate>Fri, 03 Jul 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/11-api-design/cases/error-stripe-error-object/</guid><description>&lt;p>這個案例的核心責任是提供大型 API 錯誤模型的實作設計、跟 RFC 9457 形成「標準與大廠自訂並存」的對照。&lt;/p>
&lt;h2 id="觀察">觀察&lt;/h2>
&lt;p>Stripe 錯誤物件分四個 &lt;code>type&lt;/code>（&lt;code>api_error&lt;/code> / &lt;code>card_error&lt;/code> / &lt;code>idempotency_error&lt;/code> / &lt;code>invalid_request_error&lt;/code>）；&lt;code>code&lt;/code> 是細粒度機器碼、&lt;code>decline_code&lt;/code> 透傳發卡行原因、&lt;code>param&lt;/code> 指出出錯欄位、&lt;code>message&lt;/code> 明示可直接顯示給終端使用者、附 &lt;code>doc_url&lt;/code> 與 &lt;code>request_log_url&lt;/code>。HTTP 402 專用於「參數合法但交易被拒」。&lt;/p>
&lt;h2 id="判讀">判讀&lt;/h2>
&lt;p>「路由層（type）、分支層（code）、UI 層（param + message）」做成三個正交欄位、client 端 error handling 各層各取所需。&lt;code>idempotency_error&lt;/code> 列為 first-class type、顯示冪等衝突在支付 API 是預期常態、不是邊角。Stripe 早於 RFC 7807 生態自成一格 — 標準與自訂並存是錯誤格式爭論的現實背景。&lt;/p>
&lt;h2 id="對應大綱">對應大綱&lt;/h2>
&lt;p>11.4 錯誤模型設計（anchor）、11.8 API 層冪等交叉、錯誤格式爭論文章。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>回 &lt;a href="https://tarrragon.github.io/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://docs.stripe.com/api/errors">Errors（Stripe API reference）&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>這個案例的核心責任是提供大型 API 錯誤模型的實作設計、跟 RFC 9457 形成「標準與大廠自訂並存」的對照。</p>
<h2 id="觀察">觀察</h2>
<p>Stripe 錯誤物件分四個 <code>type</code>（<code>api_error</code> / <code>card_error</code> / <code>idempotency_error</code> / <code>invalid_request_error</code>）；<code>code</code> 是細粒度機器碼、<code>decline_code</code> 透傳發卡行原因、<code>param</code> 指出出錯欄位、<code>message</code> 明示可直接顯示給終端使用者、附 <code>doc_url</code> 與 <code>request_log_url</code>。HTTP 402 專用於「參數合法但交易被拒」。</p>
<h2 id="判讀">判讀</h2>
<p>「路由層（type）、分支層（code）、UI 層（param + message）」做成三個正交欄位、client 端 error handling 各層各取所需。<code>idempotency_error</code> 列為 first-class type、顯示冪等衝突在支付 API 是預期常態、不是邊角。Stripe 早於 RFC 7807 生態自成一格 — 標準與自訂並存是錯誤格式爭論的現實背景。</p>
<h2 id="對應大綱">對應大綱</h2>
<p>11.4 錯誤模型設計（anchor）、11.8 API 層冪等交叉、錯誤格式爭論文章。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>回 <a href="/blog/backend/11-api-design/cases/" data-link-title="模組十一案例庫：API 設計與對外契約" data-link-desc="API 風格流派、版本與相容、介面語意、規範治理的已驗證公開案例集；含反例與覆蓋缺口標明">模組十一案例庫</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://docs.stripe.com/api/errors">Errors（Stripe API reference）</a></li>
</ul>
]]></content:encoded></item></channel></rss>