錯誤碼
每個錯誤回應都是同一個外殼,格式與各欄位見 請求慣例。
程式依 error.code 分支。錯誤碼在 /v1 之內不改名,也不改對應的 HTTP 狀態;message 的文字會調整。
error.retryable 是下表「可否重試」那一欄的值。true 代表原樣重送有機會成功,false 代表結果不會改變,請改動請求或改變流程。
被限流擋下時,回應帶 Retry-After,值是建議等待的秒數。
狀態碼
| 狀態碼 | 意思 |
|---|---|
| 200 | 成功,回應帶內容 |
| 201 | 已建立新資源 |
| 204 | 成功,回應沒有內容 |
| 401 | 金鑰缺少、格式錯誤、已過期或已撤銷 |
| 403 | 金鑰有效,權限範圍不足 |
| 404 | 資源不存在,或商店未開通該功能 |
| 409 | 與資源目前的狀態衝突 |
| 413 | 內文或檔案超過上限 |
| 415 | 不支援的內容型別 |
| 422 | 欄位驗證失敗 |
| 429 | 超過限流、併發或每日配額 |
| 500 | 伺服器發生非預期錯誤 |
| 503 | 商店尚未完成開通,或寫入暫停 |
錯誤碼一覽
| 錯誤碼 | HTTP | 意思 | 處理動作 | 可否重試 |
|---|---|---|---|---|
unauthorized |
401 | 金鑰缺少或無效 | 檢查 Authorization 是否為 Bearer 加一個空白加金鑰,並確認金鑰未過期 |
否 |
insufficient_scope |
403 | 金鑰少了這支端點要的權限範圍 | 讀 details 的 issue(值為 required: 加權限名),向矽羽申請具該權限的金鑰 |
否 |
not_found |
404 | 找不到這個資源 | 核對路徑與識別碼;別家商店的資源一律回這個碼 | 否 |
feature_unavailable |
404 | 商店未開通這個功能 | 先讀 GET /v1/me 的 features 再決定是否呼叫;開通請聯絡矽羽 |
否 |
validation_failed |
422 | 請求欄位不合法 | 依 details 的 field 與 issue 逐欄修正後重送 |
否 |
title_charset_unsupported |
422 | 標題含這家商店存不下的字元 | 移除 details 指出的字元;事前判斷看 limits.title_allows_emoji |
否 |
cover_image_not_owned |
422 | 封面網址不屬於這家商店 | 先用 POST /v1/media/images 上傳,再把回應的 url 原樣填入 |
否 |
content_too_large |
413 | 內文超過這家商店的上限 | 縮短 body_html 或拆成多篇;上限看 limits.body_max_bytes |
否 |
image_too_large |
413 | 圖片檔案或像素超過上限 | 在本地縮小後再上傳;上限見 限制與配額 | 否 |
unsupported_media_type |
415 | 上傳的內容不是支援的圖片格式 | 轉成 limits.image_formats 列出的格式再上傳 |
否 |
idempotency_key_reused |
409 | 同一把冪等鍵先前用在不同的內容上 | 重試時送出與第一次完全相同的內容;新的建立換一把新的鍵 | 否 |
idempotency_in_progress |
409 | 同一把冪等鍵的前一次請求還在處理 | 依 Retry-After(約 2 秒)重送,鍵與內容都保持原樣 |
是 |
duplicate_content |
409 | 十分鐘內已有標題與內文相同的文章 | 沿用 details 指出的既有文章 |
否 |
confirmation_required |
409 | 刪除需要第二段確認 | 依 文章 的刪除端點送第二次請求 | 否 |
post_is_published |
409 | 已發布或已排程的文章不能刪除 | 先呼叫下架端點,再刪除 | 否 |
post_has_product_links |
409 | 文章仍關聯著商品 | 請人在商店後台解除關聯後再刪除 | 否 |
category_name_conflict |
409 | 同一個父分類底下已有同名分類 | 沿用 details 附的既有分類識別碼,或改用別的名稱 |
否 |
image_in_use |
409 | 圖片仍被文章當封面或在內文引用 | 用 PATCH /v1/blog/posts/{id} 換掉 details 所列文章的引用,再刪一次 |
否 |
rate_limited |
429 | 這一分鐘的讀或寫次數用完 | 依 Retry-After 等待後重送,退避時加隨機抖動並設重試次數上限 |
是 |
concurrency_limited |
429 | 同時處理中的請求數超過上限 | 依 Retry-After(約 1 秒)重送,並降低併發 |
是 |
quota_exceeded |
429 | 今日新增文章或上傳圖片的配額用完 | 停止重試,改到隔天執行或減少每天的量;用量查 GET /v1/me 的 quotas |
否 |
tenant_not_certified |
503 | 商店尚未完成開通 | 聯絡矽羽完成開通,期間讀取與寫入都會被擋 | 否 |
tenant_schema_changed |
503 | 商店的內容結構已變更,寫入暫停 | 聯絡矽羽確認;期間讀取仍可用 | 否 |
internal_error |
500 | 伺服器發生非預期錯誤 | 退避後重試數次,連續失敗就停下來告警;持續發生時把 request_id 提供給矽羽 |
是 |
處理錯誤
import os
import requests
BASE_URL = "https://api.sfec.cloud"
response = requests.get(
f"{BASE_URL}/v1/blog/posts",
headers={"Authorization": f"Bearer {os.environ['SFAPP_KEY']}"},
params={"limit": 5},
timeout=10,
)
if response.ok:
print("取得", len(response.json()["data"]), "篇文章")
else:
error = response.json()["error"]
print("錯誤碼:", error["code"])
print("追蹤碼:", error["request_id"])
for item in error["details"]:
print(" -", item["field"], item["issue"])
if error["retryable"]:
print("建議等待秒數:", response.headers.get("Retry-After", "自行退避"))