AI Security Playbook

解説 / 約20分

外部サービスが止まったら、自分のアプリはどうなる?

外部画像やAPIを使う人へ。取得失敗とデータなしを分け、残す表示・止める操作・再試行と結果不明の扱いを具体例で考える。

発展 · アプリと外部APIの関係を理解し、失敗時の処理や再試行を設計する方へ。

読む前の補足
  • データなしと取得失敗を分け、表示から分かる範囲を説明する。
  • 外部依存が使えないときに残す機能と止める操作を選ぶ。
  • 再試行と、予約・決済の結果が分からない状態を区別する。
この記事の目次

AIと作った、お店の案内アプリ。写真も地図も表示できて、スマホでも確かめました。

ところがある日、写真の場所が真っ白に。営業時間の案内まで、ずっと読み込み中になっています。自分では、何も変更していません。

「昨日は動いていたのに、なぜ?」。自分の画面に見えるものでも、材料は外のサービスから届いていることがあります。その一つが使えなくなったとき、どこまで一緒に止まるかは、アプリの作りによって変わります。

この記事は、外部の画像、AI、認証、予約や決済のAPIを使うアプリを作る人に向けたものです。成功するかだけでなく、失敗したときに何を表示し、何を止めるかを考えます。

外部の画像やAPIを使うアプリを公開する前に写真が届かなくても、案内は続けられる?

32人のアバターが表示できなくなった報告

分類:事故の公表。 SCOPEWESTさんは2026年10月17日公開の記事で、外部の3Dアバター配信サービスが停止し、自分のアプリで32人のアバターを表示できなくなったと報告しています。その後、2Dの案内役へ切り替える仕組みを用意したと説明しています。SCOPEWESTさんの原記事・4.1節

ここで参照するのは、外部の配信停止による表示不能という著者自身の報告です。アバターのデータが削除された、利用者の情報が漏れた、という事例として紹介しているわけではありません。本教材が配信元の停止原因やアプリの全対策を独立に検証したものでもありません。

この経験から借りたいのは、「外部が止まったとき、自分のサービスに何を残せるか」という問いです。以下のお店のアプリ、コード、図解は、この教材用の架空例です。

自分の画面でも、材料は外から来ている

写真は画像配信、地図は地図サービス、文章の生成はAIサービス、ログインは認証サービス。こうした、アプリが動くために頼る相手を依存先と呼びます。

一つの画面にまとまっていても、同じ場所で動いているとは限りません。写真だけ取得できないのか、アプリのサーバーが応答しないのかで、影響と確認先は違います。

画面の材料を、取得先ごとに分ける

  1. お店の案内アプリ

    写真・地図・案内文を一つの画面に表示。

  2. 画像配信

    写真やアバターを取りに行く。

  3. 地図・情報API

    位置や外部の情報を取りに行く。

  4. アプリ側の案内

    独立して用意した文章や連絡先。

架空の構成です。どの依存先の失敗が、どの表示へ影響するかを見ます。

ここで考えたいのが、可用性です。必要なときにサービスや情報を使える性質を指します。秘密を守ることに加えて、使えないときの影響を小さくすることも、運営の大切な仕事です。

まず、画面に必要な情報と、その取得先を書き出します。写真の取得が終わるまで、営業時間も出さない作りになっていないか。予約ができないときも、問い合わせ先は読めるか。依存先ごとに見ます。

写真が止まったら、文章まで待たせる必要はある?

すべてがそろうまで画面全体を待たせると、補助的な機能の失敗が全体へ広がります。必要な情報を分けて、何を先に見せられるか考えます。

「掲載がない」と「調べられない」を、同じ表示にしない

営業時間を外部APIから取得する場合、「今日の案内は0件でした」と「接続できませんでした」は違います。

前者は、正常に取得した結果が空の状態。後者は、情報があるかどうかをまだ確認できていない状態です。どちらも「本日の営業はありません」と表示すると、利用者の判断を誤らせます。

空の結果と、確認できなかった結果は違う

  1. 取得を試みる

    APIの仕様に沿って、状態と内容を確認。

  2. 正常なデータ

    確認した案内を表示する。

  3. 正常だが0件

    今回の要求では掲載がないと案内。

  4. 取得や解析が失敗

    情報の有無は未確認。失敗の案内を出す。

失敗を空配列へ置き換えると、0件だったように見えてしまいます。

エラーは、原因が分かったこととも違います。たとえばFailed to fetchは通信が成立しなかったときなどに出ますが、その文字だけで提供元の全体障害とは断定できません。端末の接続、ブラウザ側の制限、設定なども確認対象です。

HTTPの応答を受け取れた場合は、状態を表す番号も手掛かりになります。HTTPステータスは、要求に対する結果を示す番号です。ただし、実際の意味はAPIの仕様と合わせて読みます。

表がはみ出す場合は、横にスクロールして読めます。

観察できたこと その時点で言えること すぐに決めつけないこと
正常な応答で、仕様どおりの空配列 この要求の結果は0件 すべての情報が永久に消えた
401 / 403 認証・アクセスの条件を確認する必要がある 提供元が全体で停止した
429 要求が多すぎるなど、利用制限を確認する 回数を増やせば通る
503 現在、要求を処理できない応答が来た データが0件だった
待ち時間を超えた 決めた時間内に結果を得られなかった 相手側で何も実行されなかった
想定と違うデータ形式 そのまま表示に使えない 空の結果として扱ってよい

JavaScriptのfetch()は、404や503などのHTTP応答だけでは失敗の例外になりません。応答の状態や内容も確認します。MDN:Fetch APIの利用

同じ空白の画面でも、案内は変わる

状況を開いて、利用者に見せる案内を見比べてください。通信は行いません。

取得できて、0件だった

表示例:掲載はありません

結果が空であることを確認できた状態です。今回の検索や対象の範囲に対する結果です。

時間内に取得できなかった

表示例:取得できませんでした

掲載があるかは未確認です。0件と表示せず、確認できなかったことを伝えます。

アクセス条件の不一致

表示例:この情報を表示できません

ログインや接続設定などを確認する場面です。取得できないからと権限を緩めません。

架空の表示例です。実際の文言は機能や利用者に合わせて決めます。

架空の応答を変えると、案内はどう変わる?

ここでは、実際のAPIを呼びません。決めておいた架空の結果を、表示用の案内へ変えるだけです。「成功時は、見出しが入った配列を返す」という架空のAPI仕様を前提にしています。

実行する場合は、手元のNode.jsの練習用ファイルで試す想定です。実キー、環境変数、ファイルの読み取り、通信、予約操作は使いません。実行しない方も、下の結果を見比べられます。

function message(result) {
  if (result.kind === "invalid") return "情報を読み取れませんでした";
  if (result.kind !== "response") return "取得できませんでした";
  const { status, items } = result;
  if (!Number.isInteger(status) || status < 100 || status > 599) {
    return "情報を読み取れませんでした";
  }
  if (status === 401 || status === 403) return "この情報を表示できません";
  if (status === 429) return "時間をおいて確認してください";
  if (status === 503) return "一時的に取得できません";
  if (status < 200 || status >= 300) return "取得できませんでした";
  if (!Array.isArray(items) ||
      !items.every(item => item && typeof item.title === "string")) {
    return "情報を読み取れませんでした";
  }
  return items.length ? `案内 ${items.length}件` : "掲載はありません";
}
const cases = [
  ["正常", { kind: "response", status: 200, items: [{ title: "架空の案内" }] }],
  ["空の結果", { kind: "response", status: 200, items: [] }],
  ["時間切れ", { kind: "timeout" }],
  ["アクセス", { kind: "response", status: 401 }],
  ["一時停止", { kind: "response", status: 503 }],
  ["形式不正", { kind: "response", status: 200, items: null }],
  ["利用制限", { kind: "response", status: 429 }],
];
for (const [name, result] of cases) console.log(name + ": " + message(result));
正常: 案内 1件
空の結果: 掲載はありません
時間切れ: 取得できませんでした
アクセス: この情報を表示できません
一時停止: 一時的に取得できません
形式不正: 情報を読み取れませんでした
利用制限: 時間をおいて確認してください

「時間切れ」「一時停止」「形式不正」が、「掲載はありません」へ落ちていないことを見ます。失敗を空の配列に置き換えるだけだと、この違いが消えてしまいます。

このコードは、表示を分ける部分だけの例です。実際の通信で起きた失敗の分類、タイムアウト、JSONの解析、API仕様に合う詳細な検証などは別に必要です。応答本文やキーをそのまま画面に出す作りにもしていません。

代わりを出せる機能と、止める機能を選ぶ

一部の機能を制限しながら、必要な役割を残すことを縮退運転と呼びます。使えないものの代わりに別の手段を出すのが、フォールバックです。

お店の写真が表示できなければ、アプリと一緒に用意した代替画像と文章を出せます。3Dの案内役が使えなくても、テキストで案内できるかもしれません。

ただし、代替画像も同じ停止中のサービスから取るなら、代わりにならない可能性があります。どこから取得するかまで確かめます。アプリ本体も配信できない障害まで、画像の代替だけで解消できるわけではありません。

残す役割と、止める操作を選ぶ

  1. 写真が取れない

    アプリ側の代替画像と文章で、案内を続ける。

  2. 権限が確かめられない

    保護した内容は返さず、案内と相談先を示す。

代わりを出すことと、確認を省いて許可することを混同しません。

一方、現在の予約枠や残高など、正確さが必要な情報を、古い値や推測で埋めてよいとは限りません。便利な見た目を残すことより、誤った判断をさせないことを優先する場面があります。

表がはみ出す場合は、横にスクロールして読めます。

機能 使えないときの選択例 確かめること
装飾の画像・アバター 独立した代替画像、テキスト 代替も同じ依存先で止まらないか
AIの文章生成 用意した案内、入力内容を保った再操作 定型文と生成結果を混同させないか
営業情報 確認できない旨と公式の問い合わせ先 古い情報を現在の確定情報に見せていないか
ログイン・権限確認 保護した内容を返さず、案内を出す エラー時に全員許可へ変わらないか
予約・決済 結果確認へ誘導し、無条件に再送しない 成功済みの操作を重ねないか

代替は、何でも埋めることではない。

画像なら代役を出す。権限が確かめられないなら保護した情報を返さない。機能ごとに、守りたいものに合う失敗時の動きを選びます。

再試行は、増やせば強くなるわけではない

タイムアウトは、結果を待つ時間に上限を設けることです。短すぎれば通常の処理まで失敗扱いになり、長すぎれば利用者を待たせ続けます。自分の処理時間と利用場面から決めます。

再試行は、一時的な失敗から戻る助けになります。ただし、すぐに何度も要求を送ると、相手の負荷や自分の利用料金を増やす場合があります。上限と間隔を設け、提供元が待機時間を指定した場合は、その仕様に従います。MDN:429、MDN:503

バックオフは、再試行の間隔を空けていく考え方です。多くの端末が同時に再送しないよう、待機時間を少しずらす設計もあります。AWS:タイムアウトと再試行の設計

再試行には、対象・時間・回数の条件がある

  1. 再送してよいか判断

    一時的か、認証の問題か。処理を重ねてもよいか。

  2. 間隔と上限を設ける

    提供元の待機指定と、SDKの再試行も確認。

  3. 戻らないときの案内

    待ち続けず、利用者の次の操作と相談先へ。

この図は無条件の自動再送を勧めるものではありません。更新系は結果確認が先に必要な場合があります。

認証の不備やデータ形式の不一致は、同じ要求を繰り返すだけでは直らないことがあります。実際には、APIの仕様、SDKが自動で行う再試行、アプリ側の再試行を合わせて見ます。いくつもの層で繰り返すと、想定以上の回数になるためです。

なお、端末で待つのをやめたことと、サーバー側の処理が取り消されたことも同じではありません。

「応答がない」と「予約されていない」は違う

予約を送った後に時間切れになったとします。失敗したのは、応答が戻る部分だけかもしれません。サーバー側では保存が完了している可能性があります。

応答が失われても、保存は済んでいるかもしれない

  1. 予約を送信

    サーバー側で保存が完了した可能性がある。

  2. 応答が届かない

    利用者側では結果が不明になる。

  3. 結果を照会する

    予約番号や一覧などで確かめ、無条件に再送しない。

架空例です。時間切れは、未実行や取り消し完了の証明ではありません。

ここで「失敗したので、もう一度押してください」とだけ案内すると、二重予約や二重決済につながることがあります。利用者に同じ操作を繰り返させる前に、注文番号や予約一覧など、結果を確かめる経路を用意します。

同じ操作を複数回頼んでも、意図した結果が一度分になるようにする性質を**冪等性(べきとうせい)**と呼びます。決済APIなどの冪等キーは、提供元の仕様に沿った実装が必要です。単にボタンを一度押せなくするだけで、すべての重複を防げるわけではありません。

この教材の演習は読み取り用の表示比較なので、実際の予約や決済を再送するコードは載せません。更新系の再試行は、提供元の仕様と結果確認の方法が分かる人と設計します。

古い情報を見せるなら、古いと分かるようにする

以前に取得した情報を保存し、後から利用する仕組みをキャッシュと呼びます。外部へ毎回取りに行かずに済む利点がありますが、更新を反映できていない可能性もあります。

写真や一般的な説明は以前のものを使えても、本日の空席や予約枠、利用者の権限は別の判断です。取得日時、有効期間、対象の利用者を考えます。権限のある人の結果を、別の人へ流用してはいけません。

表示するなら、「最終確認は○時」「現在の状況は取得できていません」のように、何が分かっている情報かを伝えます。古い情報の利用可否は機能ごとに決め、すべてのデータで同じ扱いにしません。

自分のアプリでは、3つの役割で確かめる

作る人:依存先と失敗時の動きを決める

画面ごとの画像・API・認証・保存先を整理します。必須の機能と補助的な機能を分け、失敗したときの表示、止める操作、待機の上限を決めます。AIへは成功時だけでなく、空結果・応答失敗・不正形式・結果不明の条件を伝えます。

テストする人:提供元を止めずに、失敗を再現する

自分が管理するテスト環境で、あらかじめ決めた結果を返すモックなどを使います。本物の提供元へ大量アクセスしたり、実キーを壊したりする必要はありません。

通信を行う前に、今回のような架空結果だけで表示の分岐を確認できます。実装のテストでは、遅い応答、401/403、429、503、不正形式、空の結果をそれぞれ用意します。代替の案内と次の操作を確かめ、画像と文字の読みやすさも見ます。

運用する人:どこで異常を知り、何を確認して戻すか決める

提供元の稼働状況と、自分のアプリの失敗率・待ち時間を分けて見ます。提供元の全体状況が正常でも、自分のキーや接続設定の問題は残ることがあります。

記録には時刻、処理の種類、状態コード、回数など、調査に必要な項目を選びます。キー・利用者の相談文・応答全文を、無条件に通知へ出しません。共有する範囲は会話とログの教材も参考にします。

提供元が戻った後も、自分の結果を見る

  1. 取得を確認

    状態とデータ形式が、利用中の仕様に合うか。

  2. 画面と保留操作を確認

    古い表示、結果不明の予約、重複が残らないか。

  3. 監視と連絡を続ける

    失敗率や待ち時間、必要な案内を確認。

提供元の稼働状況と、自分のアプリの回復は別々に確認します。

代替表示が出たら、テスト完了?

見た目に加えて、利用者が次に進めるか、止めるべき操作が止まるか、戻った後に古い表示や二重処理が残らないかも確かめます。

公開前に、失敗した状態で確かめる

  • 依存先: 各機能の取得先と、必須・補助の役割を整理し、同じ障害で代替まで止まらないか確認した。
  • 状態の区別: 正常・空結果・アクセス条件・利用制限・通信失敗・形式不正で、表示と次の操作が分かれることをテストした。
  • 待機と再試行: 待機の上限、再試行の回数と間隔、SDKとアプリ側の重複を確認した。
  • 更新系: 予約・決済の結果不明を成功や未実行と決めつけず、結果を確認する経路がある。
  • 古い情報: 何を再利用できるか、取得日時・有効期間・利用者の範囲を決めた。
  • 復旧: 外部が戻った後の取得、画面、保留操作、監視と相談先を確認した。

次は、公開前の手順へ組み込む

公開前の確認へ、成功時だけでなく失敗時の結果も加えます。すでに停止や予想外の請求が起きている場合は、異常時の対応で、対象と事実を絞って相談を進めます。

依存先を把握できたら、次はそのライブラリや実行環境を、公開後にどう更新していくかが課題になります。

一次資料と参考記事

情報確認日:2026年10月24日。実際の応答形式、再試行、冪等性、待機時間の指定は、使うAPIとSDKの公式資料で確認してください。

書いた人

マーケティングの現場から、AIをつくり、仕事で使う側へ。実際に試したことや、つまずいたことを、図解と具体例で伝えています。

この記事のURL:https://nekoai-lab.github.io/ai-security-playbook/learn/external-service-failures/

続けて読む

シリーズ · 全14記事のうち13記事目

次に読むAIに削除・交換を案内された後まで。失効・履歴・保全・復旧を分け、対象に応じた初動と再開条件を整理する。
読む順番・ルートを見るAIで作って公開する基本
  1. AIに渡す情報を、送る前に分ける
  2. 「学習しない」AIなら、仕事の個人情報を使える?
  3. AIで作ったアプリの、動く場所を知る
  4. AIに「APIキーを設定して」と言われた。どこに入れるのが正解?
  5. APIキーを替えたのに、古いキーが使われている?
  6. ログインと、データを使う権限を分ける
  7. 公開したのは、本当に見せたいファイルだけ?
  8. コードには書いていない。名前やメールは、どこから公開される?
  9. 緑のチェックが付いた。その変更を公開していい?
  10. AIレビューが合格した。その結果を、どこまで信じていい?
  11. AIにデプロイを頼んだ。その認証情報は、どこまでできる?
  12. AIで作ったサービスを、公開する前に確かめる
  13. 外部サービスが止まったら、自分のアプリはどうなる?
  14. 漏えい・異常・消失に気づいたとき、最初にすること

更新・公開後の運用の教材へ戻る →