本文へスキップ
Sursa Developers

エラー

Sursaは、リクエストが成功したかどうかをHTTPステータスコードで示し、失敗した理由を安定したエラーコードで示します。エラーレスポンスはすべて同じ形式です。

{
  "detail": "the cursor is malformed or was issued for different filters",
  "error": {
    "code": "api.cursor_invalid",
    "message": "the cursor is malformed or was issued for different filters",
    "details": {"parameter": "cursor"},
    "request_id": "01JB7Q2M4X9V3K8T5N6P0R1S2A",
    "doc_url": "https://developers.sursa.com/errors/api.cursor_invalid"
  }
}
フィールド意味
error.code<area>.<reason>形式の安定したコード。コードの分岐にはこれを使ってください。
error.message人が読むための英語の説明。言い回しはいつでも変わることがあるので、解析しないでください。
error.detailsパラメーター、上限、再試行のタイミングなど、エラーについての構造化された情報。フィールドはコードによって異なります。
error.request_idこのリクエストのID。レスポンスヘッダーX-Request-Idと同じです。
error.doc_urlこのポータルにある、このコードについてのページ。
detail既存の連携システムのために残している、以前の形式のエラー。新しいコードではerrorを読んでください。

各コードには、エラーコードの下に専用のページがあり、HTTPステータス、detailsのフィールド、そのコードを返すエンドポイントが記載されています。APIリファレンスの各エンドポイントのページには、そのエンドポイントが返すことのあるコードが記載されています。

HTTPステータス

ステータス意味
200、201、202、204リクエストは成功しました。202は、リクエストが受け付けられ、まだ処理中であることを意味します。
400リクエストの形式が正しくありません。パラメーター、カーソル、metadata、Idempotency-Keyのいずれかが不正です。リクエストを修正してください。
401有効なキーがありません。認証を参照してください。
402カードの数やストレージなど、組織のプランの上限に達しました。
403キーは有効ですが、この操作は許可されていません。ロール、スコープ、またはプランの機能が不足しています。
404オブジェクトが存在しないか、キーで参照できません。
409リクエストが現在の状態と競合しています。external_idの重複、開いたままの同期、同じIdempotency-Keyのリクエストの実行中などです。
410オブジェクトの期限が切れているか(24時間を過ぎたアップロードなど)、ルートが廃止されています。
413ボディまたはファイルが大きすぎます。
415ファイルの種類がサポートされていないか、内容が名前と一致しません。
422リクエストの形式は正しいものの、内容が無効です。フィールドの不足や範囲外の値などです。
429リクエストが多すぎるか、クォータを使い切りました。レート制限とクォータを参照してください。
500、502、503、504Sursa側の問題です。後で再試行してください。

バリデーションエラー

コードvalidation.failedの422では、detailに失敗したすべてのフィールドが、それぞれの位置とともに示されます。

{
  "detail": [
    {"loc": ["body", "title"], "msg": "String should have at least 1 character", "type": "string_too_short"}
  ],
  "error": {"code": "validation.failed", "message": "request validation failed", "request_id": "01JB7…"}
}

再試行すべきエラー

エラー再試行するか
ネットワークエラーまたはタイムアウトで、レスポンスがないはい。同じIdempotency-Keyで再試行します。
429はい。Retry-Afterの秒数だけ待ってから再試行します。
500、502、503、504はい。指数バックオフで再試行します:1秒、2秒、4秒…最大1分まで、ランダムなジッターを加えます。
409 api.idempotency_in_progressはい。Retry-Afterの後に再試行します。このキーでの最初のリクエストがまだ実行中です。
その他の4xxいいえ。同じリクエストはまた失敗します。先にリクエストを修正してください。

POSTとPATCHのリクエストは、必ずIdempotency-Keyを付けて再試行してください。そうすれば、レスポンスが失われた後の再試行で2つ目のオブジェクトが作成されることはありません。GET、PUT、DELETEはそのまま再試行しても安全です。

// Retries a request on network errors, 429 and 5xx. The same Idempotency-Key is sent on every attempt.
async function withRetries(send, attempts = 5) {
  for (let attempt = 1; ; attempt++) {
    let response;
    try {
      response = await send();
    } catch (error) {
      if (attempt >= attempts) throw error;
    }
    if (response && response.status !== 429 && response.status < 500) return response;
    if (attempt >= attempts) return response;
    const retryAfter = Number(response?.headers.get("Retry-After"));
    const backoff = Math.min(60, 2 ** (attempt - 1)) * (0.8 + Math.random() * 0.4);
    await new Promise((resolve) => setTimeout(resolve, 1000 * (retryAfter || backoff)));
  }
}

const key = crypto.randomUUID();
const response = await withRetries(() =>
  fetch("https://api.sursa.com/v1/cards/text", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.SURSA_API_KEY}`,
      "Content-Type": "application/json",
      "Idempotency-Key": key,
    },
    body: JSON.stringify({ title: "Refund policy", body_markdown: "Refunds take 14 days." }),
  }),
);

問題の報告

成功したかどうかにかかわらず、すべてのレスポンスにはX-Request-Idヘッダーがあり、すべてのエラーのerror.request_idには同じ値が入ります。自社のリクエストの記録と一緒にログに残してください。リクエストについてサポートに問い合わせるときは、そのリクエストIDを伝えてください。そのリクエストの記録にすぐたどり着けます。

未知のコードの扱い

新しいエラーコードは、APIのどのバージョンでも追加されることがあります。未知のコードはHTTPステータスで判断してください。未知の4xxはそのままでは成功しないリクエスト、未知の5xxは一時的な問題として扱います。