全国オンライン対応受付 平日 9:00-18:00
お問い合わせ

開発記録 / toi-toi-toi制作記 / Vol.30 toi-toi-toi制作記 Vol.4

記事 01

予約が「失敗しました」と言い続けた理由——muteHttpExceptionsがなかった

「予約に失敗しました。時間をおいて再度お試しください。」

2026-06-09 公開

「予約に失敗しました。時間をおいて再度お試しください。」

このエラーメッセージが表示されていると報告が来た。予約フォームから送信すると、確定画面に進まずエラーになる。

実際に試した。エラーが再現した。


エラーの連鎖を追いかけた

フロントエンドの submitBooking()fetch('/api/register') を呼ぶ。EC2のFastAPIが受け取り、GASに転送する。GASが処理してJSONを返す。FastAPIがそれをフロントエンドに返す。

エラーはフロントエンドの d.successundefined のときに表示される判定があった。

d.success がなぜ undefined になるのかを追った。

FastAPIのログを確認すると r.json() の呼び出しで例外が発生していた。GASが返したレスポンスがJSONではなく、HTMLだった。

GASがHTMLエラーページを返していた。


GASが例外を投げていた

GASのコードを確認した。pushMessage() 関数の中でLINE APIを呼んでいた。

function pushMessage(userId, messages) {
  UrlFetchApp.fetch('https://api.line.me/v2/bot/message/push', {
    method: 'post',
    contentType: 'application/json',
    headers: { 'Authorization': 'Bearer ' + LINE_CHANNEL_ACCESS_TOKEN },
    payload: JSON.stringify({ to: userId, messages: messages })
  });
}

muteHttpExceptions の指定がなかった。

GASの UrlFetchApp.fetch() はデフォルトで、レスポンスのHTTPステータスが4xx・5xxの場合に例外をスローする。LINE APIが何らかの理由でエラーを返すと、GASが例外を投げる。

doGet 全体に try/catch もなかった。例外が捕捉されず、GASはHTMLのエラーページをレスポンスとして返す。FastAPIがそれをJSONとして解析しようとして失敗する。エラーオブジェクトが返る。フロントエンドが「失敗しました」を表示する。


修正は1行

function pushMessage(userId, messages) {
  UrlFetchApp.fetch('https://api.line.me/v2/bot/message/push', {
    method: 'post',
    contentType: 'application/json',
    headers: { 'Authorization': 'Bearer ' + LINE_CHANNEL_ACCESS_TOKEN },
    payload: JSON.stringify({ to: userId, messages: messages }),
    muteHttpExceptions: true
  });
}

muteHttpExceptions: true を追加した。これでLINE APIが4xx・5xxを返しても例外はスローされない。ステータスコードを無視して処理を続ける。

replyMessage() も同じ構造だったので同様に修正した。


doGetにtry/catchを追加した

muteHttpExceptions で例外の発生源は塞いだ。それとは別に、doGet 全体を try/catch で囲んだ。

どこかで予期しない例外が発生してもHTMLエラーページを返さない。代わりにJSONで {success: false, error: "..."} を返す。FastAPIが正常に解析できる。フロントエンドが適切なエラーメッセージを表示できる。

エラーの連鎖を一箇所で断ち切る設計だ。


予約は通っていた

調査の過程で気づいたことがある。「失敗しました」と表示されていたにもかかわらず、スプレッドシートには予約が記録されていた。

GASが例外を投げたのはLINE通知を送る段階だった。予約の記録自体は完了していた。通知が失敗したことでGASが落ち、フロントエンドにエラーが伝わった。

ユーザーは「予約できなかった」と思いながら、実際には予約が入っていた。混乱のもとになる状態だった。通知の失敗が予約の失敗に見えてしまう設計の問題だった。


*シンプルシステム株式会社 代表 伊藤勝彦*