目次
まず「未適用」と「結果不明」を分ける
複数の変更を一度に送ったあとエラーが返ると、「途中まで反映されたのでは」と心配になります。最初に区別したいのは、APIが失敗を明確に返したケースと、リクエストを送った後に通信が切れて結果を受け取れなかったケースです。
Sheets APIのbatch requestでは、サブリクエストは適用前に検証されます。無効なリクエストが一つでもあれば、そのbatch全体が失敗し、変更は適用されません。したがって、明確な400系の入力エラーなど、APIがbatch不成立を返している場合は「一部だけ成功した」と決めつけて補修を始める必要はありません。問題のrequestを特定し、入力を直してから、意図したbatchを再送します。
一方、タイムアウトや接続切断では、サーバーが更新を適用した後に応答だけ失われた可能性を否定できません。この場合に同じ更新をすぐ再送すると、appendや新しいシート作成など、同じ操作を二重に行う危険があります。まず現在値を読み戻し、どの変更が反映されているかを確認してから次の操作を決めます。
本稿の前提はSheets API v4の spreadsheets.batchUpdate です。values APIの values.batchUpdate やApps Script、独自のラッパーを使っている場合、request形式やエラーの見え方が異なることがあります。エンドポイントと送信したJSONを最初に記録してください。
送信前に残す最小の記録
再試行を安全にするには、送信前の意図と送信後の観測を分離して残します。バッチごとに次を保存してください。
表は横にスクロールできます
| 項目 | 記録する内容 |
|---|---|
| 対象 | spreadsheet ID、対象sheet ID、更新する範囲 |
| 実行 | 実行ID、送信日時、実行者またはworker |
| 入力 | request配列、field mask、更新前の重要セル値 |
| 期待 | 成功時に成立するセル・シート・書式の状態 |
| 結果 | HTTP status、API error、response body、repliesの有無 |
| 再試行 | 最初の実行ID、確認した範囲、次の実行ID |
アクセストークン、サービスアカウント秘密鍵、OAuth refresh tokenなどの認証情報は記録に含めません。リクエスト本文に個人情報や機密値がある場合は、保存先と閲覧権限を限定し、ログへ丸ごと複製しないでください。
request配列の何番目かを追えるよう、アプリケーション側では各サブリクエストに連番または業務キーを付けたmanifestを作ります。Sheets API自身が任意のrequest IDを受け付けると仮定せず、ローカルのmanifestとrequest indexを対応させます。たとえば「request 2: タブの追加」「request 3: B4:D8更新」のように、順番と対象を説明可能にします。
明確なvalidation errorが返った場合
まずHTTP statusとGoogle API errorのmessage、status、detailsを保存します。responseにrequest indexが明記されないこともあるため、エラー文だけで失敗した位置を特定できない場合があります。送信したrequest配列を順に検査し、次のような誤りを見ます。
-
sheetIdが存在しない、または別のspreadsheetのIDを使っている
-
start/end row・column indexの境界が逆、範囲外、または対象sheetのgridより大きい
-
updateCellsのfield maskと更新値が一致しない -
repeatCellの範囲やuserEnteredFormatのフィールド名が不正 -
addSheetで既存と重なるsheetIdや不正なプロパティを指定した -
数値・日付・数式などの値型が、列の期待と異なる
-
一つ前のrequestが作るsheetやrangeを、後続requestが誤ったIDで参照している
「最後に追加した変更が悪い」と推測してその一つだけ直すのではなく、request配列全体を再検証します。複数の入力が同時に不正な場合もあります。修正後は、更新するセルとfield maskを小さく保ち、意図しない既存値・書式を置き換えないことを確認します。
batch内のrequestは配列の順で実行されます。先のrequestが作成するsheetなどに、後のrequestを依存させることはできます。ただし、batch全体が成功したときに限って一緒に適用されます。dependent requestのsheetId、range、field maskを、実行前にmanifest上で照合してください。
応答を受け取れなかった場合のreadback
通信断・クライアントtimeout・応答解析エラーでは、結果を「失敗」と決めず「不明」とします。再送前に、変更の種類に応じた範囲を読み戻してください。
表は横にスクロールできます
| 変更操作 | 確認するもの | 再試行前の判断 |
|---|---|---|
| 固定範囲のセル更新 | 対象範囲の全セル値・数式・必要な書式 | 期待値と一致なら成功扱いにし、同じ書込みを重ねない。一部不一致なら現在値を比較して修復範囲を限定 |
addSheet | spreadsheetのsheet一覧、sheetIdとtitle | 作成済みなら再作成しない。名前だけ同じでもsheetIdが異なる場合は別状態として確認 |
appendCells / 末尾追加 | 追加行の業務キー、前後の行、実行ID | 同一業務キーや実行IDの行があるか調べ、二重追加を防止 |
| 書式変更 | 対象範囲の実際の書式 | 値だけで成功判定せず、必要な書式フィールドをreadback |
| 複数操作 | 各操作の完了条件と副作用 | 操作ごとに状態を判定し、既適用分を飛ばして未適用分だけを処理 |
固定範囲への同じ値の設定は比較的冪等に設計しやすいものの、常に無条件で再送してよいわけではありません。途中で人や別workerが編集した可能性があるなら、送信前snapshotと現在値を比較し、他者の変更を上書きしないようにします。
読み戻しでは「更新対象セルだけ」ではなく、必要に応じて隣接範囲や数式・書式も見ます。行の挿入やソートなどで位置が変わる仕組みなら、行番号だけでは同一レコードと判定できません。Article IDや業務キーなど、安定した識別子で確認します。
エラー位置とrepliesの読み方
batchUpdateが成功すると、responseのrepliesはrequest配列と同じ順番で並びます。requestによっては返答内容がなく、その位置の要素が空になります。したがって、空のreplyを失敗と解釈してはいけません。HTTP成功とAPI responseを全体として確認し、request indexとreply indexを対応づけます。
反対に、batchがvalidationで失敗したときに、部分適用を前提に「成功したreplyまで反映済み」と扱うのは危険です。公式仕様上、一つでも無効なrequestがあればbatch全体が失敗します。まずエラーresponseと、通信が明確に失敗応答として届いたかを確認します。レスポンス自体が欠落している場合だけ、前節のreadbackで状態を確定します。
API wrapperがresponseの一部を捨てていると、Google APIから返ったstatusやdetailsが見えないことがあります。SDKやHTTP clientの例外を業務エラーへ変換する箇所を確認し、HTTP status、Google error body、ローカルrequest indexを失わないようにします。ただし記事の手順だけで特定SDKの例外形式を断定しないでください。使用中のclient libraryのversionに対応した公式referenceを確認します。
requestを小さく切り分ける方法
invalid requestの特定に時間がかかる場合、元データを保護したうえで、検証用コピーやテスト用spreadsheetでrequestを分割します。元の本番データに対して、原因探しのための試行錯誤を繰り返さないでください。
-
元のrequest配列、更新前snapshot、再現条件を保存する。
-
使用中のAPI endpointとrequest typeを確定する。
-
各requestを単独で構文・ID・range・field mask検証する。
-
検証環境で、依存関係のないrequest群へ分割して再現する。
-
原因requestを特定したら、必要な最小修正を行う。
-
元の業務上の原子性が必要なら、最終的には一つのbatchへ戻し、全体を再検証する。
-
実データへ送る直前に対象ID・範囲・更新前値を再確認する。
分割テストは原因特定のためであり、本来一体であるべき更新を本番で複数batchへ分ける提案ではありません。分割すると、別クライアントが間に編集できる時間が生まれ、望む整合性が失われる場合があります。
具体例:3セル更新の一つが不正
例として、A2に実行状態、B2に結果件数、C2に最終確認時刻を同一batchで設定する処理を考えます。これは説明用の架空データで、実際のAPI結果ではありません。
request 1はA2に文字列「complete」、request 2はB2に数値12、request 3は存在しないsheet IDのC2を更新しようとしています。Google APIがvalidation errorを返した場合、request 3だけが失敗してA2・B2が既に書かれたとは判断しません。batch全体が適用されないため、まず正しいsheet IDをmanifestとspreadsheet metadataから確認し、request 1〜3の全範囲を再点検して再送します。
ここでネットワークが切れ、HTTP応答を受け取れなかった場合は、同じ判断をしてはいけません。A2:C2をreadbackし、期待する3つの値が揃っているかを確認します。3つすべて一致なら、実行済みと記録します。すべて旧値なら、他者の編集がないことを確認したうえで再実行できます。一部だけ一致する状態なら、通常のbatch atomicityの想定と異なる原因(別操作・別worker・参照範囲誤りなど)を調べ、元値と現在値を比較して解決するまで自動再送しません。
再送前チェックリスト
次の項目をすべて確認してから再送します。
-
対象spreadsheet IDとsheet IDがArticle IDや対象名称に合っている
-
API endpointが
spreadsheets.batchUpdateか、別のbatch APIか確定した -
失敗が明確なAPI errorか、応答を失ったunknown outcomeか区別した
-
request配列の全件をID・range・型・field maskまで再検証した
-
既存値を上書きしないための更新前snapshotがある
-
request indexと変更内容を照合できるmanifestがある
-
retryが二重append、二重作成、二重送信を起こさない
-
readbackで対象業務キーと更新後の期待状態を確認できる
-
必要なreviewerが差分と影響範囲を確認した
-
再送の実行IDと結果を記録できる
どれかが確認できなければ、再送を保留して不足情報を取得します。エラーを解消することより、対象範囲を正しく保ち、二重適用や別行への誤書込みを防ぐことを優先します。
作業記録に残す結果
復旧記録には、対象ID、request配列の要約、失敗分類、readback範囲、修正したrequest、再送有無、readback結果を残します。元のAPI responseと必要なログへの参照も保存しますが、認証情報は除外します。
記事や業務データの更新では、row順や配列indexをレコードIDの代わりにしません。たとえばArticle行を更新する場合は、対象Article IDを再検索し、IDと期待タイトルを照合してから対象セルを決めます。batchのindexはリクエストの順番を指すだけで、業務レコードの識別子ではありません。
最後に、処理後は書込み応答だけに頼らず、対象ID・セル・数式・statusを再取得して、期待状態と一致したことを記録します。readbackが一致しない場合は、成功扱いにせず、外部編集・誤ったsheet/range・数式計算待ちなどを切り分けます。通常のセル更新、書式変更、シート追加の完了条件を一つの「HTTP 200」にまとめず、操作ごとに定義してください。
公式情報と確認範囲
本稿はGoogle Sheets API v4のbatch requestと spreadsheets.batchUpdate の仕様を2026-10-15に確認し、atomic validation、request順序、response repliesの対応を説明しています。掲載した例は仕様を理解するための例であり、実際のスプレッドシートへAPI callを実行した結果ではありません。
出典・適用範囲
対象はGoogle Sheets API v4の spreadsheets.batchUpdate です。個々のclient libraryの例外型、認証scope、quota、values API、Apps Script固有の挙動は、本稿だけでは確定しません。実環境へrequestを送信したり、SDKを実行したりしていません。client libraryとendpointを特定した後、実装のerror mappingとreadback手順をテスト環境で確認してください。
