Vercel の Serverless Function がタイムアウトする
Vercel Function は実行時間に上限がある。
既定は全プラン 300 秒で、Hobby は 300 秒が上限、Pro / Enterprise は maxDuration で 800 秒まで延ばせる。maxDuration 設定、Promise.all での並列化、ジョブキューへの退避、ストリーミングで切断回避の 4 系統で解決する。
公開: 更新:
要約
Vercel Function のタイムアウト上限はプランと runtime で決まっている。
全プラン既定 300 秒 / Hobby は 300 秒が上限 / Pro・Enterprise は maxDuration で最大 800 秒、が出発点。長すぎる処理は maxDuration で延ばす、Promise.all で並列化する、ジョブキューに退避する、ストリーミングで切断を防ぐ、の 4 系統で解決する。
よくある原因
- 既定 300 秒の上限: 何も指定しなければ全プランで 300 秒で打ち切られ、Hobby はここから延長できない。
maxDuration未指定: Pro / Enterprise ではコード側でmaxDurationを指定しないと既定値のままで、想定より早く切られる。- 外部 API への直列呼び出し: 3 API × 4 秒の直列 fetch を書いていて、簡単に 10 秒を超え、
maxDurationを小さく設定していると上限に当たる。 - Edge Runtime の 25 秒: Edge Runtime は 25 秒以内にレスポンスを返し始めないとストリーミングを維持できない仕様。
長時間タスクには不向き。 - キュー退避をしていない: メール送信・PDF 生成・LLM 呼び出しなどの重い処理を 1 リクエスト内で完結させようとしている。
解決策
1. maxDuration を route で指定する
// app/api/heavy/route.ts (Pro plan)
export const maxDuration = 60;
export async function GET() {
const data = await heavyJob();
return Response.json(data);
}公式の Configuring Functions / Duration(新しいタブで開く) のとおり、Pro 以上は最大 800 秒まで設定可能(1800 秒は beta で関数ごとの指定が必要)。
Hobby では maxDuration を書いても上限は 300 秒のまま。
2. 並列化して時間を縮める
const [a, b, c] = await Promise.all([fetchA(), fetchB(), fetchC()]);3 つの直列 fetch(各 4 秒)が並列化で 4 秒前後に縮む。
fetch 同士に依存関係が無いケースでは最初に検討する。
3. Edge Runtime と Node Serverless の選び方
公式の Runtimes(新しいタブで開く) のとおり、軽量 API(認証、レスポンス整形、低レイテンシ)は Edge(コールドスタートほぼゼロ)、Node API 必須 / 長時間 / 重い処理は Node Serverless が原則。
Edge では 25 秒以内にレスポンスを返し始めれば、その後 300 秒までストリーミングを継続できる。
4. ジョブキューへ退避
長時間処理は Function 内で完結させず、QStash / Inngest / Upstash Queue 等の外部キューに enqueue だけして即 return する。
クライアントは別エンドポイントで進捗をポーリングする構成にする。