「保存できません。直してください」とエージェントへ伝えると、いくつかのファイルが変更されます。それでも保存できず、もう一度頼むと、今度は別のファイルが変わる。この往復が続くと、どの変更に意味があったのかも分からなくなります。

「保存できない」は、画面で見えている症状です。その一言だけでは、ボタンが反応していないのか、API通信が失敗したのか、データベースが書き込みを拒否したのかまでは分かりません。エージェントも、手掛かりが少なければ可能性の高そうな場所から推測することになります。

エラー調査で行うのは、いきなり正解を当てることではありません。同じ失敗を起こせる条件をそろえ、「ここまでは動いた」「この先で失敗した」と範囲を狭めていきます。コードを自力で修正できなくても、調査の流れを理解していれば、エージェントが事実を確認したのか、推測だけで変更したのかを見分けられます。

「動かない」を再現手順へ変える

最初に、誰が試しても同じ現象を起こせる手順を作ります。次のように、環境、利用者、操作、期待した結果、実際の結果を分けて書きます。

  • 環境: Preview(https://preview.example.com
  • 利用者: 一般ユーザー
  1. ログインする
  2. /articles/newを開く
  3. タイトルに「テスト」と入力する
  4. 保存を押す
  • 期待: 記事詳細へ移動する
  • 実際: ボタンが読み込み中のままになる
  • 発生: 3回中3回

この情報があると、エージェントは「保存機能を調べる」という広い作業ではなく、一般ユーザーがPreview環境で新規記事を保存するときの処理に対象を絞れます。

「ときどき失敗する」場合は、発生回数も重要です。10回中1回なのか、毎回なのかで、調べる対象が変わります。失敗した時刻、ブラウザ、端末、入力内容、利用者の権限も残しておくと、成功した場合との違いを比べられます。

再現手順は長ければよいわけではありません。不要な操作を一つずつ外し、失敗に必要な条件だけを残します。たとえば、画像を添付しなくても失敗するなら、画像アップロードは最初の調査対象から外せます。一方で、再現できないまま条件を次々と変えると、同じ問題を見ているのか分からなくなります。まず一つの手順を固定し、その後で条件を一つずつ変えます。

どの環境で失敗したかを固定する

同じコードでも、動かす場所によって設定やデータが違います。エラー報告では、次のどの環境なのかを明記します。

  • Local: 自分のPCで動かしている環境。URLは通常localhostから始まります。
  • Preview: Pull Requestやブランチごとに用意される確認用の環境です。
  • Staging: 本番に近い設定で最終確認するための環境です。
  • Production: 実際の利用者が使う本番環境です。

Localだけで失敗するなら、ローカルの環境変数、依存パッケージ、開発用データを確認します。Previewだけなら、そのブランチに対応するSecretsやデータベースが不足しているかもしれません。Productionだけなら、本番のデータ量、権限、ドメイン、外部サービスの設定などが候補になります。

環境名だけでなく、確認したおおよその時刻と、分かる場合はブランチ名やデプロイの識別情報も残します。別の版を見ながら調査すると、エージェントがコード上では直っている問題を追い続けることがあります。

ビルドが成功したこと、デプロイが成功したこと、公開URLで目的の操作が成功したことは、それぞれ別の確認です。「CIが緑だから本番でも直った」とは限りません。どの段階まで確かめたのかを分けて伝えます。

同じコードでもLocal、Preview、Staging、Productionでは環境変数、Secrets、データが異なることを示す図

失敗地点を順番に分ける

Webアプリの操作は、画面だけで完結しているとは限りません。保存ボタンを押したあと、ブラウザがAPIへリクエストを送り、サーバーが認証や入力を確認し、データベースへ書き込み、その結果を画面へ返します。画面に「保存できません」と表示されても、原因はこの途中のどこにでもあります。

最初は、失敗地点を次の四つに分けます。

  1. 画面を表示する前
  2. ブラウザでの表示や操作
  3. API通信
  4. サーバー・データベース・外部サービス

画面を表示する前

開発サーバーが起動しない、ビルドが失敗する、ページを開いた瞬間に例外が出る、といった問題です。ブラウザの操作へ進む前に止まっているため、ターミナルやデプロイログを確認します。

この段階では、エラーの末尾だけでなく、最初に失敗したコマンドと、その直後に表示されたメッセージを残します。大量のエラーが続いていても、最初の一件が原因で後続の処理も失敗している場合があります。

ブラウザでの表示や操作

ページは開くがボタンが反応しない、入力した値が画面へ反映されない、クリック直後に表示が消える、といった問題です。ブラウザ内のJavaScriptで止まっている可能性があるため、Consoleを確認します。

ボタンを押したときにAPI通信が一件も発生していなければ、サーバーより前で止まっていると判断できます。逆に、通信が発生していれば、少なくともクリックからリクエスト送信までの処理は進んでいます。

API通信

ブラウザからリクエストは送られたものの、URLやHTTPメソッドが違う、入力が不足している、認証や権限で拒否された、サーバーがエラーを返した、といった問題です。NetworkパネルでRequestとResponseを確認します。

ここでは「通信が失敗した」という言い方だけで終わらせず、どのURLへ、どのmethodで送り、どのstatus codeが返ったかまで残します。それだけで、エージェントが確認すべきコードや設定をかなり絞れます。

サーバー・データベース・外部サービス

APIまでは届いたものの、サーバー内の処理で止まっている状態です。データベースの権限拒否、マイグレーション不足、外部APIのtimeout、Secretsの設定漏れ、利用上限などが候補になります。サーバーログ、データベースのログ、外部サービスの管理画面を確認します。

一つの問題が、複数の場所に別の症状を出すこともあります。データベースのtimeoutが、APIでは500、画面では「保存できません」と表示される、といった形です。画面から推測するのではなく、処理を順にたどり、最初に失敗した場所を探します。

ブラウザのConsoleを見る

Chromeでは、ページを右クリックして「検証」を開き、Consoleタブを選ぶと、ブラウザ側で発生したエラーを確認できます。Consoleを開いたまま再現手順を実行すると、その操作で増えたメッセージを見つけやすくなります。

たとえば、次のエラーが表示されたとします。

TypeError: Cannot read properties of undefined (reading 'title')

これは、titleを読もうとした相手がundefinedだったことを表しています。原因がすぐ分からなくても、エラーメッセージを省略せずエージェントへ渡せば、どの値が入っていなかったのかをコードから追えます。

エラーの下には、処理が通った関数やファイルを示すstack traceが続くことがあります。

at ArticleForm (ArticleForm.tsx:42)
at NewArticlePage (page.tsx:18)

最初に見るのは、自分のプロジェクト内のファイル名と行番号です。この例ならArticleForm.tsxの42行目が出発点になります。ライブラリ内部の行が何十件も続いていても、最初からすべて理解する必要はありません。

Consoleには赤いエラーだけでなく警告も表示されます。警告がすべて不具合の原因とは限らないため、再現操作をした時刻と、症状が出た瞬間に増えたものを優先します。また、同じ原因から複数のエラーが続くこともあります。最後のエラーだけでなく、最初に現れたエラーを確認します。

Consoleに何も表示されないことも手掛かりです。ブラウザ側で例外が出ていないなら、次はNetworkで通信が発生したかを見ます。「Consoleにエラーがない」と「問題がない」は同じではありません。

Networkでリクエストを確認する

保存やログインなど、API通信を伴う操作では、開発者ツールのNetworkタブを開いてから再現します。通信が多い場合は、Fetch/XHRに絞るとAPIリクエストを見つけやすくなります。

対象のリクエストを選び、次の項目を確認します。

  • NameまたはURL: どのAPIへ送ったか
  • Method: GETPOSTPATCHDELETEなど、どの操作か
  • Status: サーバーが返した結果
  • Request Payload: ブラウザから送った内容
  • Response: サーバーから返った内容
  • Timing: 返答までの時間と、どこで待っていたか

保存リクエストそのものが見当たらなければ、ブラウザ側の処理を調べます。リクエストがある場合は、status codeを手掛かりに次の場所を選びます。

  • 400: 送った値の形式や必須項目を確認する
  • 401: ログイン情報やセッションを確認する
  • 403: ログイン後の権限や所有者条件を確認する
  • 404: URL、対象ID、デプロイされているAPIを確認する
  • 409: 同時更新や重複データなどの競合を確認する
  • 429: 呼び出し回数の上限やretryを確認する
  • 500台: サーバーログや外部サービスを確認する

リクエストがpendingのままなら、サーバー処理が終わっていない、外部APIを待っている、応答を返し忘れている、といった可能性があります。canceledなら、画面遷移や再送によってブラウザ側で中断されたことも考えられます。

NetworkにはCookie、Token、入力した個人情報が含まれる場合があります。スクリーンショット、Copy as cURL、HARファイルを共有する前に、中身を確認します。必要なのは秘密の値そのものではなく、どのリクエストがどの結果になったかです。

サーバーログを時刻で合わせる

Networkでリクエストが送られたことを確認できたら、同じ時刻のサーバーログを探します。Localなら開発サーバーを起動したターミナル、PreviewやProductionならデプロイ先のログ画面を確認します。

2026-08-25T12:34:56Z POST /api/articles 500

ブラウザで操作した時刻と、ログの時刻が近ければ、同じリクエストである可能性が高くなります。ただし、多くの利用者が同時に使う環境では、時刻だけでは区別できません。その場合はrequest IDやtrace IDが役立ちます。

requestId=req_abc123 route=/api/articles status=500
requestId=req_abc123 error=DATABASE_TIMEOUT

同じrequest IDをたどると、一回の保存操作がどの処理まで進み、どこで失敗したかを追えます。エージェントがログを追加した場合は、処理名、結果、エラーの種類、安全な識別IDが残っているかを確認します。

Console、Network、サーバーとデータベースのログを順に確認し、時刻とrequest IDで同じ操作を追う図

該当時刻のログが一件もない場合もあります。そのときは、リクエストがサーバーまで届いていない、別の環境のログを見ている、別のrouteへ送っている、といった可能性を先に確認します。

ログへパスワード、APIキー、Cookie、Token、入力された個人情報をそのまま残してはいけません。「調査しやすくするため」という理由でも、秘密情報を丸ごと表示する変更は避けます。

データベースと外部サービスの失敗を見る

サーバーログからデータベースや外部APIで止まったことが分かったら、その対象に合わせて確認します。データベースでは、テーブルや列が存在しない、書き込み権限がない、同じ値を重複して登録しようとした、問い合わせに時間がかかった、といったエラーが出ます。

新しいコードだけが先にデプロイされ、必要なマイグレーションが本番データベースへ適用されていない場合もあります。このときはコードを直す前に、どの環境へどのマイグレーションが適用済みかを確認します。エージェントへ実行を任せる場合も、本番へ変更を加える操作なのかを先に聞きます。

外部APIでは、認証情報の期限切れ、利用上限、timeout、相手側の障害などがあります。こちらのコードだけを何度変更しても直らないことがあるため、外部サービスの返答や状態も証拠として分けて残します。

本番データを直接書き換えて「直るか試す」のは、原因調査とデータ変更が混ざる危険な方法です。まず読み取りで状態を確認し、必要ならLocalやPreviewで再現します。

エラーメッセージを分けて考える

同じ失敗でも、利用者、ブラウザ、開発者が必要とする情報は違います。

  • 利用者向け: 保存できませんでした。時間をおいてもう一度お試しください。
  • APIの返答: status: 500と、安全に公開できるエラーコード
  • 内部ログ: database timeout after 3000ms, requestId=req_abc123

利用者向けの画面にSQL、ファイルパス、stack trace、接続情報まで表示すると、攻撃の手掛かりや秘密情報を漏らす可能性があります。一方、内部ログまで「エラー」としか書かれていなければ、どの処理で何が起きたのか調べられません。

エージェントがエラー処理を変更したときは、画面の文言だけでなく、APIが適切なstatus codeを返しているか、内部ログに調査可能な情報が残るかも確認します。

利用者の画面、API response、内部ログで、エラー情報の詳しさと公開範囲を分ける図

原因と症状を区別する

画面に症状が現れた場所と、原因がある場所は一致しないことがあります。

  • 画面で確認できた事実: 記事一覧が空になっている
  • 原因候補:
    • APIが500を返している
    • 認証期限が切れて401になった
    • データベースの検索条件が間違っている
    • APIは正しい配列を返したが、画面側で表示していない

この時点で確認できているのは「一覧が空」という事実だけです。NetworkでAPIのResponseを見るまでは、APIやデータベースが原因だとは断定できません。正しいデータが返っていれば画面側を、返っていなければAPIより先を調べます。

エージェントの報告では、「確認できた事実」「そこから立てた仮説」「まだ確認していないこと」を分けてもらいます。「おそらく認証が原因です」と書かれていても、status codeやログを見ていなければ仮説のままです。

仮説を一つずつ試す

原因候補が複数あるときは、それぞれを区別できる小さな確認を考えます。調査は次の順番で進めます。

  1. 同じ条件で問題を再現する
  2. Console、Network、ログから証拠を集める
  3. 証拠で確かめられる仮説を一つ選ぶ
  4. 仮説に関係する箇所だけを変える
  5. 最初と同じ手順で結果を確認する

再現、証拠収集、仮説選択、一つだけの変更、同じ手順での確認を左から右へ進め、直らない場合だけ仮説へ戻る調査手順

たとえば、「認証期限切れで保存できない」という仮説を考えたとします。

  • 仮説: 認証期限切れで保存できない
  • 確認: 同じ操作のstatus codeを見る
  • 結果: 403だった
  • 判断: 未認証を表す401ではないため、権限拒否の条件を調べる

この確認では、まだコードを変更していません。事実が仮説と合わなければ、その仮説を捨てて次へ進めます。仮説を確かめる前に修正を始めると、偶然動いたのか、根本原因を直したのか判断できません。

一度に画面、API、データベース設定をすべて変更すると、どの変更が効いたか分からなくなります。変更しても直らなかった場合は、その変更を残したまま次の変更を重ねず、必要がなければ元に戻します。調査用に加えたログや一時的な分岐も、原因が分かったあとに整理します。

環境変数を全部表示する、権限チェックを無効にする、本番データベースを直接書き換える、といった方法は、確認できることより危険の方が大きくなります。エージェントが提案した場合は、より狭い権限と安全な環境で同じ仮説を確かめられないか確認します。

最近の変更から範囲を絞る

昨日まで動いていた機能が今日壊れたなら、その間に変わったものを確認します。Gitの差分だけでなく、コードの外にある設定も対象です。

  • 変更されたファイル
  • パッケージとpackage-lock.json
  • 環境変数やSecrets
  • データベースのマイグレーション
  • デプロイ先やドメインの設定
  • 外部サービスの仕様、利用上限、障害

変更履歴は原因を断定するものではありませんが、調査の出発点になります。保存APIが壊れた直前に認証処理が変わっていれば、権限やsessionを優先して確認できます。反対に、コードの差分がなくても、Secretの期限切れや外部サービスの障害で動かなくなることがあります。

変更を一つ前へ戻すと直るかを確認する方法もあります。ただし、Productionのコード、環境変数、データベースを戻す操作は影響が大きいため、対象環境と戻る範囲を確認してから行います。データベースはコードと同じように簡単に戻せるとは限りません。

「直った」を同じ手順で確かめる

修正後は、最初に書いた再現手順を同じ環境、同じ利用者、同じ入力で実行します。途中で条件を変えると、修正によって直ったのか、別の条件だから成功したのか分かりません。

  • 修正前: Previewで一般ユーザーが3回試し、3回失敗
  • 修正後: 同じPreview、同じ権限、同じ入力で3回成功

画面で成功したように見えるだけでなく、必要に応じてNetworkのstatus code、保存されたデータ、サーバーログも確認します。たとえば、画面は記事詳細へ移動しても、同じ記事が二重に保存されていれば別の問題が残っています。

次に、変更した処理の周辺も確認します。一般ユーザーの保存を直したことで管理者の操作が壊れていないか、権限エラーを避けるために認可を外していないか、timeoutを延ばしただけで処理が極端に遅くなっていないかを見ます。

不具合を再現する自動テストを追加できるなら、修正前のコードで失敗し、修正後のコードで成功することを確認します。testbuildが成功したことと、PreviewやProductionで利用者の操作が成功したことは別なので、どこまで確認したかを記録します。

同じPreview環境、権限、入力で修正前の失敗と修正後の成功を比べ、通信、保存データ、権限、周辺操作も確認する図

エージェントへ渡す調査メモ

エージェントへ依頼するときは、原因の予想より先に、確認できた事実を渡します。次の形にすると、再現と証拠集めから始めやすくなります。

環境: URL: ブランチまたはデプロイ: 利用者の権限: 発生時刻:

再現手順: 1. 2. 3.

期待した結果: 実際の結果: 発生回数:

Console: NetworkのURL・method・status: サーバーログのrequest ID:

直前の変更: 確認済みのこと: まだ確認していないこと:

秘密情報や個人情報は値を伏せます。必要な場合も、値そのものではなく「その環境変数が設定されているか」「期待する権限が付いているか」を確認させます。

エージェントの調査を確認する

エージェントから報告を受けたら、変更されたファイルの数より、調査の筋道を確認します。

  • 対象環境と再現手順を固定したか
  • 修正前に実際の失敗を再現できたか
  • Console、Network、サーバーログのどこまで処理が進んだか
  • 最初に発生したエラーを確認したか
  • 確認できた事実と仮説を分けているか
  • 一つの仮説に対して一つの確認を行ったか
  • 無関係なファイルや設定まで変更していないか
  • 調査のために認証、認可、入力検証を外していないか
  • ログへ秘密情報や個人情報を追加していないか
  • 修正前後を同じ手順で比較したか
  • 自動テスト、Preview、Productionのどこまで確認したか
  • 確認できなかった環境や条件を明記したか

調査を始めるときは、次のように依頼できます。

まだ修正せず、まずこの問題を同じ環境と手順で再現してください。 Console、Network、サーバーログのどこまで処理が進んだかを、確認できた事実だけで報告してください。

原因候補が複数あるときは、変更より先に確認方法を出してもらいます。

原因候補を挙げ、それぞれを区別できる最小の確認方法を書いてください。 本番データの変更、権限チェックの無効化、秘密情報の表示は行わないでください。

修正後は、成功したという結論だけではなく、比較した証拠を求めます。

修正前の失敗と修正後の成功を、同じ環境・同じ再現手順で示してください。 影響した可能性がある機能と、まだ確認していない項目も分けてください。

エラー調査は、原因を言い当てる作業ではありません。同じ失敗を起こし、どの境界まで正常だったかを確認し、証拠に合う仮説だけを小さく試す作業です。その記録があれば、エージェントの修正を「ファイルが変わった」ではなく、「同じ条件で失敗しなくなった」と判断できます。