個人開発の傍らで、Claude API を使って複数サイトの下書きを自動生成するパイプラインを毎日動かしています。長時間にわたって Claude を走らせ続けていると、「応答が返ってこない」「処理が途中で止まる」場面に何度も出くわします。最初のころは、原因を切り分けないまま、そのたびに闇雲にリロードしていました。
ですが原因は数種類しかなく、しかも見分け方さえ分かれば対処はあっという間です。大切なのは「待てば解決すること」と「自分で直さないと永遠に解決しないこと」を取り違えないこと。ここでは、その切り分けを軸に整理します。
まず「どこで止まっているか」を切り分ける
応答がないと一括りにしてしまいがちですが、止まっている場所によって対処はまったく変わります。最初に自分がどちらの状況かを判断してください。
- ブラウザ/アプリで使っている(claude.ai やデスクトップ版)→ 待つ・リロード・キャッシュなど、手元の操作で直ることがほとんど
- API を自分のコードから叩いている → タイムアウトや再試行など、コード側の作り込みで直す
この二つを混同すると、「コードを直すべき場面でブラウザを再起動し続ける」といった遠回りに陥ります。順に見ていきます。
ブラウザ・アプリ側:止まったときの確認順序
手元のアプリで応答が来ないときは、上から順に試すと早く切り分けられます。
1. まず数十秒待つ。 これがいちばん見落とされます。短い返答なら10〜30秒、長い文章や大きなコンテキストの読み込みなら数分かかることもあります。プログレス表示が動いているなら、たいてい処理は進んでいます。
2. 別のサイトを開いて回線を確認する。 自分の通信が切れているだけ、ということは意外と多いものです。
3. ページをリロードする。 一時的な描画の不具合は、これで直ることが多いです。会話は保存されているので消えません。
4. シークレットウィンドウで開く。 直ったなら、原因は拡張機能です。広告ブロッカーやスクリプト制御系の拡張が、応答の表示を止めていることがあります。
5. キャッシュをクリアする。 上記で直らないときの最後の手段です。古いスクリプトが残っていると、画面が更新されないことがあります。
6. ステータスページを見る。 ここまでで直らなければ、自分側ではなくサービス側かもしれません。Anthropic Status で障害情報を確認してください。
ピーク時間帯も覚えておくと役立ちます。複数サイトのスケジュール投稿を運用していて実感するのは、日本時間の夜(おおよそ19〜23時)は混みやすく、早朝6〜9時は明らかに軽いということです。重い処理は時間帯をずらすだけで体感が変わります。
API 側:コードで止まる原因と直し方
自分のプログラムから Claude API を呼んでいて応答が返らないときは、ブラウザの操作では何も解決しません。原因はだいたい次の3つに集約されます。
タイムアウトが短すぎる
長いコンテキストや複雑な推論は、応答に時間がかかります。HTTP クライアント側の既定タイムアウトが短いと、まだ生成中なのに自分のコードが先に諦めてしまいます。SDK のタイムアウトを明示的に延ばしてください。
import anthropic
# 既定より長めにタイムアウトを設定する
client = anthropic.Anthropic(timeout=120.0)
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=4096,
messages=[{"role": "user", "content": "長文の要約をお願いします ..."}],
)「止まっている」ように見えて、実は自分のタイムアウトで切っていた——というのが、私が最初にやった勘違いでした。
長い応答で待ち時間が読めない
返答が長くなるほど、最後の一文字まで待たされます。途中で本当に止まったのか、まだ生成中なのかを見分けるには、ストリーミングが有効です。トークンが流れてくるかどうかで「生きているか」がすぐ分かります。
with client.messages.stream(
model="claude-sonnet-4-6",
max_tokens=4096,
messages=[{"role": "user", "content": "詳しく説明してください ..."}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)文字が一切流れてこないなら接続側の問題、流れているなら単に長いだけ、と判断できます。
厄介なのは、最初は流れていたトークンが途中でぱたりと止まる場合です。接続そのものは生きているのにデータだけ来ない、という状態は、全体のタイムアウトだけでは捕まえきれません。最後にトークンを受け取ってからの経過時間を別のタイマーで見張り、動きが止まったらストリームを閉じてやると、この「無言のハング」を早い段階で断ち切れます。
import threading
import anthropic
client = anthropic.Anthropic(timeout=120.0)
def stream_with_idle_watchdog(messages, idle_limit=30.0):
"""最後の受信から idle_limit 秒動きがなければストリームを閉じる"""
chunks = []
with client.messages.stream(
model="claude-sonnet-4-6",
max_tokens=4096,
messages=messages,
) as stream:
def on_idle():
stream.close() # ブロックしている反復を解除する
timer = threading.Timer(idle_limit, on_idle)
timer.start()
try:
for text in stream.text_stream:
timer.cancel()
chunks.append(text)
timer = threading.Timer(idle_limit, on_idle)
timer.start()
finally:
timer.cancel()
return "".join(chunks)全体のタイムアウトが「生成が長すぎる」を捉えるのに対して、このアイドル監視は「流れが途切れた」を捉えます。役割が別なので、両方を持っておくと、止まったのか待てばよいのかの判断を機械に任せられます。私自身のパイプラインでも、夜間バッチで稀に起きる途中停止は、この二段構えでほぼ手放しになりました。
一時的なエラーで再試行していない
混雑時の 429(レート制限)やサーバー側の 5xx は、少し待って投げ直せば通ることがほとんどです。逆に 400 系(リクエストの誤り)は何度投げても通らないので、すぐ止めて原因を直す必要があります。この区別を入れておくと、止まったように見える現象の多くが自動で解消します。
import time
import anthropic
client = anthropic.Anthropic(timeout=120.0)
def create_with_retry(**kwargs):
for attempt in range(4):
try:
return client.messages.create(**kwargs)
except anthropic.RateLimitError:
wait = 2 ** attempt # 1, 2, 4, 8 秒と待ち時間を伸ばす
time.sleep(wait)
except anthropic.InternalServerError:
time.sleep(2 ** attempt)
except anthropic.BadRequestError:
raise # 400 系は再試行せず即座に止める
raise RuntimeError("再試行の上限に達しました")私のパイプラインでも、夜間にまとめて動かすと 429 がときどき出ます。この指数バックオフを入れてからは、手で再実行することがほとんどなくなりました。
「待つべき」か「直すべき」かの早見表
迷ったときは、この基準で判断してください。
- プログレスが動いている/トークンが流れている → 待つ(正常です)
- ブラウザで固まった・拡張を入れている → リロード/シークレットで確認
- 自分のコードで一定時間後に必ず切れる → タイムアウトを延ばす
- 混雑時間に断続的に失敗する → 再試行(バックオフ)を入れる
400系のエラーが返る → 待っても無駄。リクエストを直す
次の一歩
もし API を自分のコードから使っているなら、今日はまずタイムアウトの明示と、429・5xx への指数バックオフだけ入れてみてください。この二つだけで、「止まった」と感じる場面の大半は静かに消えていきます。
私も最初はリロードを連打していた側でした。同じ回り道をしている方の手間が、少しでも減れば嬉しいです。