Skip to content

非公式本サイトは非公式の日本語ドキュメントであり、Cloudflare 公式サイトではありません。最新情報はdevelopers.cloudflare.comをご確認ください。

リトライ

最終更新 Markdown で表示Agent セットアップ

失敗した操作を、指数バックオフとジッターで再試行します。Agents SDK は、スケジュールタスク、キュータスク、および独自コード向けの汎用 this.retry() メソッドに、組み込みのリトライを提供します。

概要

外部 API の呼び出し、他サービスとのやり取り、バックグラウンドタスクの実行では、一時的な失敗がよく起きます。リトライシステムは、これらを自動で処理します。

  • 指数バックオフ — 再試行のたびに、待ち時間が長くなります
  • ジッター — 遅延をランダム化し、一斉再試行(サンダリングハード)を防ぎます
  • 設定可能 — 試行回数、遅延、上限を呼び出しごとに調整できます
  • 組み込み — schedule、queue、workflow の操作は自動で再試行します

クイックスタート

任意の非同期操作を再試行するには、this.retry() を使います。

import { Agent } from "agents";

export class MyAgent extends Agent {
	async fetchWithRetry(url) {
		const response = await this.retry(async () => {
			const res = await fetch(url);
			if (!res.ok) throw new Error(`HTTP ${res.status}`);
			return res.json();
		});

		return response;
	}
}
import { Agent } from "agents";

export class MyAgent extends Agent {
	async fetchWithRetry(url: string) {
		const response = await this.retry(async () => {
			const res = await fetch(url);
			if (!res.ok) throw new Error(`HTTP ${res.status}`);
			return res.json();
		});

		return response;
	}
}

デフォルトでは、this.retry() はジッター付き指数バックオフで、最大 3 回再試行します。

this.retry()

retry() メソッドは、すべての Agent インスタンスで使えます。デフォルトでは、投げられたエラーすべてに対して、指定した関数を再試行します。

async retry<T>(
  fn: (attempt: number) => Promise<T>,
  options?: RetryOptions & {
    shouldRetry?: (err: unknown, nextAttempt: number) => boolean;
  }
): Promise<T>

パラメーター:

  • fn — 再試行する非同期関数です。現在の試行番号(1 始まり)を受け取ります。
  • options — 任意のリトライ設定です(後述の RetryOptions を参照)。オプションは先行検証されます。不正な値はすぐ例外になります。
  • options.shouldRetry — 投げられたエラーと次の試行番号を受け取る、任意の述語です。false を返すと、すぐ再試行を止めます。未指定なら、すべてのエラーを再試行します。

戻り値: 成功時の fn の結果です。

例外: すべての試行が失敗したとき、または shouldRetryfalse を返したとき、最後のエラーを投げます。

基本的なリトライ:

const data = await this.retry(() => fetch("https://api.example.com/data"));
const data = await this.retry(() => fetch("https://api.example.com/data"));

カスタムのリトライオプション:

const data = await this.retry(
	async () => {
		const res = await fetch("https://slow-api.example.com/data");
		if (!res.ok) throw new Error(`HTTP ${res.status}`);
		return res.json();
	},
	{
		maxAttempts: 5,
		baseDelayMs: 500,
		maxDelayMs: 10000,
	},
);
const data = await this.retry(
	async () => {
		const res = await fetch("https://slow-api.example.com/data");
		if (!res.ok) throw new Error(`HTTP ${res.status}`);
		return res.json();
	},
	{
		maxAttempts: 5,
		baseDelayMs: 500,
		maxDelayMs: 10000,
	},
);

試行番号を使う:

const result = await this.retry(async (attempt) => {
	console.log(`Attempt ${attempt}...`);
	return await this.callExternalService();
});
const result = await this.retry(async (attempt) => {
	console.log(`Attempt ${attempt}...`);
	return await this.callExternalService();
});

shouldRetry で選択的に再試行する:

特定のエラーでは再試行を止めるには、shouldRetry を使います。述語はエラーと次の試行番号の両方を受け取ります。

const data = await this.retry(
	async () => {
		const res = await fetch("https://api.example.com/data");
		if (!res.ok) throw new HttpError(res.status, await res.text());
		return res.json();
	},
	{
		maxAttempts: 5,
		shouldRetry: (err, nextAttempt) => {
			// Do not retry 4xx client errors — our request is wrong
			if (err instanceof HttpError && err.status >= 400 && err.status < 500) {
				return false;
			}
			return true; // retry everything else (5xx, network errors, etc.)
		},
	},
);
const data = await this.retry(
	async () => {
		const res = await fetch("https://api.example.com/data");
		if (!res.ok) throw new HttpError(res.status, await res.text());
		return res.json();
	},
	{
		maxAttempts: 5,
		shouldRetry: (err, nextAttempt) => {
			// Do not retry 4xx client errors — our request is wrong
			if (err instanceof HttpError && err.status >= 400 && err.status < 500) {
				return false;
			}
			return true; // retry everything else (5xx, network errors, etc.)
		},
	},
);

スケジュールでのリトライ

スケジュール作成時に、リトライオプションを渡します。

// Retry up to 5 times if the callback fails
await this.schedule(
	"processTask",
	60,
	{ taskId: "123" },
	{
		retry: { maxAttempts: 5 },
	},
);

// Retry with custom backoff
await this.schedule(
	new Date("2026-03-01T09:00:00Z"),
	"sendReport",
	{},
	{
		retry: {
			maxAttempts: 3,
			baseDelayMs: 1000,
			maxDelayMs: 30000,
		},
	},
);

// Cron with retries
await this.schedule(
	"0 8 * * *",
	"dailyDigest",
	{},
	{
		retry: { maxAttempts: 3 },
	},
);

// Interval with retries
await this.scheduleEvery(
	30,
	"poll",
	{ source: "api" },
	{
		retry: { maxAttempts: 5, baseDelayMs: 200 },
	},
);
// Retry up to 5 times if the callback fails
await this.schedule(
	"processTask",
	60,
	{ taskId: "123" },
	{
		retry: { maxAttempts: 5 },
	},
);

// Retry with custom backoff
await this.schedule(
	new Date("2026-03-01T09:00:00Z"),
	"sendReport",
	{},
	{
		retry: {
			maxAttempts: 3,
			baseDelayMs: 1000,
			maxDelayMs: 30000,
		},
	},
);

// Cron with retries
await this.schedule(
	"0 8 * * *",
	"dailyDigest",
	{},
	{
		retry: { maxAttempts: 3 },
	},
);

// Interval with retries
await this.scheduleEvery(
	30,
	"poll",
	{ source: "api" },
	{
		retry: { maxAttempts: 5, baseDelayMs: 200 },
	},
);

コールバックが例外を投げると、リトライオプションに従って再試行します。すべての試行が失敗すると、エラーはログに残り、onError() へ渡されます。成功・失敗にかかわらず、ワンタイムスケジュールは削除され、cron / interval は再スケジュールされます。

キューでのリトライ

キューへタスクを追加するときに、リトライオプションを渡します。

await this.queue(
	"sendEmail",
	{ to: "user@example.com" },
	{
		retry: { maxAttempts: 5 },
	},
);

await this.queue("processWebhook", webhookData, {
	retry: {
		maxAttempts: 3,
		baseDelayMs: 500,
		maxDelayMs: 5000,
	},
});
await this.queue(
	"sendEmail",
	{ to: "user@example.com" },
	{
		retry: { maxAttempts: 5 },
	},
);

await this.queue("processWebhook", webhookData, {
	retry: {
		maxAttempts: 3,
		baseDelayMs: 500,
		maxDelayMs: 5000,
	},
});

コールバックが例外を投げると、タスクがデキューされる前に再試行します。すべての試行を使い切ると、タスクはデキューされ、エラーがログに残ります。

検証

リトライオプションは、this.retry()queue()schedule()scheduleEvery() の呼び出し時に先行検証されます。不正なオプションは、実行時まで待たずにすぐ例外になります。

// Throws immediately: "retry.maxAttempts must be >= 1"
await this.queue("sendEmail", data, {
	retry: { maxAttempts: 0 },
});

// Throws immediately: "retry.baseDelayMs must be > 0"
await this.schedule(
	60,
	"process",
	{},
	{
		retry: { baseDelayMs: -100 },
	},
);

// Throws immediately: "retry.maxAttempts must be an integer"
await this.retry(() => fetch(url), { maxAttempts: 2.5 });

// Throws immediately: "retry.baseDelayMs must be <= retry.maxDelayMs"
// because baseDelayMs: 5000 exceeds the default maxDelayMs: 3000
await this.queue("sendEmail", data, {
	retry: { baseDelayMs: 5000 },
});
// Throws immediately: "retry.maxAttempts must be >= 1"
await this.queue("sendEmail", data, {
	retry: { maxAttempts: 0 },
});

// Throws immediately: "retry.baseDelayMs must be > 0"
await this.schedule(
	60,
	"process",
	{},
	{
		retry: { baseDelayMs: -100 },
	},
);

// Throws immediately: "retry.maxAttempts must be an integer"
await this.retry(() => fetch(url), { maxAttempts: 2.5 });

// Throws immediately: "retry.baseDelayMs must be <= retry.maxDelayMs"
// because baseDelayMs: 5000 exceeds the default maxDelayMs: 3000
await this.queue("sendEmail", data, {
	retry: { baseDelayMs: 5000 },
});

検証は、フィールド間の制約を調べる前に、部分指定のオプションをクラスレベルまたは組み込みのデフォルトで補完します。そのため、解決後の maxDelayMs が 3000 のとき、{ baseDelayMs: 5000 } は実行時まで待たずにすぐ検出されます。

デフォルトの動作

明示的なリトライオプションがなくても、スケジュールとキューのコールバックは、妥当なデフォルトで再試行されます。

設定 デフォルト
maxAttempts 3
baseDelayMs 100
maxDelayMs 3000

これらのデフォルトは this.retry()queue()schedule()scheduleEvery() に適用されます。呼び出しごとのオプションが優先されます。

クラスレベルのデフォルト

static options で、エージェント全体のデフォルトを上書きできます。

class MyAgent extends Agent {
	static options = {
		retry: { maxAttempts: 5, baseDelayMs: 200, maxDelayMs: 5000 },
	};
}
class MyAgent extends Agent {
	static options = {
		retry: { maxAttempts: 5, baseDelayMs: 200, maxDelayMs: 5000 },
	};
}

変更したいフィールドだけ指定すれば十分です。未指定のフィールドは、組み込みデフォルトに戻ります。

class MyAgent extends Agent {
	// Only override maxAttempts; baseDelayMs (100) and maxDelayMs (3000) stay default
	static options = {
		retry: { maxAttempts: 10 },
	};
}
class MyAgent extends Agent {
	// Only override maxAttempts; baseDelayMs (100) and maxDelayMs (3000) stay default
	static options = {
		retry: { maxAttempts: 10 },
	};
}

クラスレベルのデフォルトは、呼び出し側がリトライオプションを指定しないときのフォールバックです。呼び出しごとのオプションが常に優先されます。

// Uses class-level defaults (10 attempts)
await this.retry(() => fetch(url));

// Overrides to 2 attempts for this specific call
await this.retry(() => fetch(url), { maxAttempts: 2 });
// Uses class-level defaults (10 attempts)
await this.retry(() => fetch(url));

// Overrides to 2 attempts for this specific call
await this.retry(() => fetch(url), { maxAttempts: 2 });

特定のタスクでリトライを無効にするには、maxAttempts: 1 を設定します。

await this.schedule(
	60,
	"oneShot",
	{},
	{
		retry: { maxAttempts: 1 },
	},
);
await this.schedule(
	60,
	"oneShot",
	{},
	{
		retry: { maxAttempts: 1 },
	},
);

RetryOptions

interface RetryOptions {
	/** Maximum number of attempts (including the first). Must be an integer >= 1. Default: 3 */
	maxAttempts?: number;
	/** Base delay in milliseconds for exponential backoff. Must be > 0 and <= maxDelayMs. Default: 100 */
	baseDelayMs?: number;
	/** Maximum delay cap in milliseconds. Must be > 0. Default: 3000 */
	maxDelayMs?: number;
}

リトライ間の遅延は、フルジッター指数バックオフ です。

delay = random(0, min(2^attempt * baseDelayMs, maxDelayMs))

そのため、初期の再試行は速く(多くの場合 200ms 未満)、後の再試行はバックオフして、障害中のサービスを圧倒しません。ランダム化(ジッター)により、複数エージェントが同じ瞬間に再試行するのを防ぎます。

仕組み

バックオフ戦略

リトライシステムは、AWS Architecture Blog の「Full Jitter」戦略を使います。デフォルト設定で 3 回試行する場合は次のとおりです。

試行 上限 実際の遅延
1 min(2^1 * 100, 3000) = 200ms random(0, 200ms)
2 min(2^2 * 100, 3000) = 400ms random(0, 400ms)
3 (再試行なし — 最終試行)

maxAttempts: 5baseDelayMs: 500 の場合:

試行 上限 実際の遅延
1 min(2 * 500, 3000) = 1000ms random(0, 1000ms)
2 min(4 * 500, 3000) = 2000ms random(0, 2000ms)
3 min(8 * 500, 3000) = 3000ms random(0, 3000ms)
4 min(16 * 500, 3000) = 3000ms random(0, 3000ms)
5 (再試行なし — 最終試行)

MCP サーバーのリトライ

MCP サーバーを追加するとき、接続と再接続の試行にリトライオプションを設定できます。

await this.addMcpServer("github", "https://mcp.github.com", {
	retry: { maxAttempts: 5, baseDelayMs: 1000, maxDelayMs: 10000 },
});
await this.addMcpServer("github", "https://mcp.github.com", {
	retry: { maxAttempts: 5, baseDelayMs: 1000, maxDelayMs: 10000 },
});

これらのオプションは永続化され、次のときに使われます。

  • ハイバネーション後のサーバー接続の復元
  • OAuth 完了後の接続確立

デフォルトは 3 回試行、ベース遅延 500ms、最大遅延 5s です。

パターン

ログ付きリトライ

class MyAgent extends Agent {
	async resilientTask(payload) {
		try {
			const result = await this.retry(
				async (attempt) => {
					if (attempt > 1) {
						console.log(`Retrying ${payload.url} (attempt ${attempt})...`);
					}
					const res = await fetch(payload.url);
					if (!res.ok) throw new Error(`HTTP ${res.status}`);
					return res.json();
				},
				{ maxAttempts: 5 },
			);
			console.log("Success:", result);
		} catch (e) {
			console.error("All retries failed:", e);
		}
	}
}
class MyAgent extends Agent {
	async resilientTask(payload: { url: string }) {
		try {
			const result = await this.retry(
				async (attempt) => {
					if (attempt > 1) {
						console.log(`Retrying ${payload.url} (attempt ${attempt})...`);
					}
					const res = await fetch(payload.url);
					if (!res.ok) throw new Error(`HTTP ${res.status}`);
					return res.json();
				},
				{ maxAttempts: 5 },
			);
			console.log("Success:", result);
		} catch (e) {
			console.error("All retries failed:", e);
		}
	}
}

フォールバック付きリトライ

class MyAgent extends Agent {
	async fetchData() {
		try {
			return await this.retry(
				() => fetch("https://primary-api.example.com/data"),
				{ maxAttempts: 3, baseDelayMs: 200 },
			);
		} catch {
			// Primary failed, try fallback
			return await this.retry(
				() => fetch("https://fallback-api.example.com/data"),
				{ maxAttempts: 2 },
			);
		}
	}
}
class MyAgent extends Agent {
	async fetchData() {
		try {
			return await this.retry(
				() => fetch("https://primary-api.example.com/data"),
				{ maxAttempts: 3, baseDelayMs: 200 },
			);
		} catch {
			// Primary failed, try fallback
			return await this.retry(
				() => fetch("https://fallback-api.example.com/data"),
				{ maxAttempts: 2 },
			);
		}
	}
}

リトライとスケジュールを組み合わせる

回復に数分や数時間かかる操作では、即時の再試行に this.retry()、遅延した再試行に this.schedule() を組み合わせます。

class MyAgent extends Agent {
	async syncData(payload) {
		const attempt = payload.attempt ?? 1;

		try {
			// Immediate retries for transient failures (seconds)
			await this.retry(() => this.fetchAndProcess(payload.source), {
				maxAttempts: 3,
				baseDelayMs: 1000,
			});
		} catch (e) {
			if (attempt >= 5) {
				console.error("Giving up after 5 scheduled attempts");
				return;
			}

			// Schedule a retry in 5 minutes for longer outages
			const delaySeconds = 300 * attempt;
			await this.schedule(delaySeconds, "syncData", {
				source: payload.source,
				attempt: attempt + 1,
			});
			console.log(`Scheduled retry ${attempt + 1} in ${delaySeconds}s`);
		}
	}
}
class MyAgent extends Agent {
	async syncData(payload: { source: string; attempt?: number }) {
		const attempt = payload.attempt ?? 1;

		try {
			// Immediate retries for transient failures (seconds)
			await this.retry(() => this.fetchAndProcess(payload.source), {
				maxAttempts: 3,
				baseDelayMs: 1000,
			});
		} catch (e) {
			if (attempt >= 5) {
				console.error("Giving up after 5 scheduled attempts");
				return;
			}

			// Schedule a retry in 5 minutes for longer outages
			const delaySeconds = 300 * attempt;
			await this.schedule(delaySeconds, "syncData", {
				source: payload.source,
				attempt: attempt + 1,
			});
			console.log(`Scheduled retry ${attempt + 1} in ${delaySeconds}s`);
		}
	}
}

制限事項

  • デッドレターキューはありません。 キューまたはスケジュールのタスクがすべてのリトライに失敗すると、タスクは削除されます。失敗したタスクを追跡するには、独自の永続化を実装してください。
  • リトライ遅延中はエージェントがブロックされます。 バックオフ遅延のあいだ、Durable Object は起動したままアイドルです。短い遅延(3 秒未満)なら問題ありません。より長い回復時間には、代わりに this.schedule() を使います。
  • キューのリトライはヘッドオブラインブロッキングです。 キュー項目は順次処理されます。1 件が長い遅延で再試行中だと、後続の全項目がブロックされます。独立したリトライが必要な場合は、queue() のタスク単位オプションではなく、コールバック内で this.retry() を使います。
  • サーキットブレーカーはありません。 リトライシステムは、呼び出しをまたいだ失敗率を追跡しません。サービスが継続的に落ちていると、各タスクが独立してリトライ予算を使い切ります。
  • shouldRetrythis.retry() だけで使えます。 関数はデータベースへシリアライズできないため、shouldRetry 述語は schedule()queue() では使えません。スケジュール / キューのタスクでは、再試行できないエラーをコールバック内で処理してください。

次のステップ

役に立ちましたか?