🤖 AIとつくる

Astro の dev サーバーが ImageNotFound から戻らない — 原因は画像インポートの対応表、Astro 5 → 7 で解消

約12,700字
dev サーバーのターミナル出力 (ImageNotFound が繰り返し表示されている) を背景に、「公開のたびに ImageNotFound、再起動するまで戻らない、原因は消えない対応表、5.18.1 → 7.2.2 で解消」の文字

このブログは Astro という静的サイトジェネレーターで作っていて、執筆中の記事はレビュー用の dev サーバー (localhost:4321) で確認しています。その dev サーバーで、記事を公開するたびに記事に関わるページがすべて 500 エラーになり、再起動するまで戻らない、という状態が2週間ほど続いていました。その間は「公開したら dev を再起動する」という運用で回していました。

原因は、Astro が dev サーバーの動作中に生成している画像インポートの対応表が、一度登録した項目を消さないまま積み上がることでした。Claude Code に調べてもらうと、この不具合は Astro 6.2.0 ですでに修正されていました。使っていたのは 5.18.1 です。公開フロー自体を変える回避策も検討していたのですが、必要がなくなったので取りやめ、Astro 7.2.2 まで更新しました。

私がやったのは、症状の共有と、どのバージョンまで上げるかの判断と、更新後の目視確認です。原因の特定・上流の調査・移行作業・検証は Claude Code に任せました。dev は再起動すれば動くので、目の前の困りごとというより、今後の公開作業で起きないようにしたい、というつもりで頼みました。Astro のバージョンアップ自体はどのみち必要だったので、この機会にまとめてやることにしました。

以降は 🤖 Claude Code の執筆です。冒頭の画像の背景は、壊れたときにブログ主が記録した出力を再生して撮影したものです。


何が起きていたか

このブログでは、執筆中の記事を src/content/posts/_drafts/ に置き、公開するときに1階層上の src/content/posts/ へ移動しています。移動にあわせて、frontmatter に書いてあるヒーロー画像の相対パスも ../../../ から ../../ へ書き換えます。

dev サーバーの不具合は前の記事でも扱いました。公開作業とレビューが同じ作業ディレクトリで衝突する問題は、Claude Code と並列でブログを書いていたら dev サーバーの取り合いになったので、git worktree で解決した で公開作業を git worktree に分離して解決しています。今回はその後に残った、別の壊れ方です。

記事の移動とパスの書き換えをすると、起動しっぱなしにしているレビュー用の dev サーバーが 500 を返すようになります。公開した記事以外のページも壊れます。壊れた状態で各ページを開くと次のとおりでした。

/                          → 500
/blog/                     → 500
/blog/<公開した記事>/       → 500
/privacy/                  → 200

コンテンツコレクションの画像を扱うページがすべて 500 になり、記事と関係のない静的ページは生き残ります。ターミナルには次のエラーが繰り返し出ます (ブラウザにも同じ内容のエラー画面が出ます)。

[ERROR] [ImageNotFound] Could not find requested image
`../../../assets/posts/synapusyu-summer-piano/hero.jpg`. Does it exist?

パスを直したあとも、記事ファイルを touch しても、このエラーは出続けます。今回の調査では、壊れた dev サーバーをそのまま残しておいたところ、40分たっても戻りませんでした。

どうしたか: Astro を 7.2.2 に更新した

原因は Astro 本体の dev サーバーにあり、6.2.0 で修正されていました。使っていた 5.18.1 から 7.2.2 へ更新することで解消しています。記事の mdx ファイルとアセットには手を入れていません (更新で触ったのは設定と依存です)。

更新後 (7.2.2) に公開作業を再現して再計測した結果です (3行目のタイプミスの再現は 6.4.8 で確認)。

git mv _drafts/x.mdx x.mdx (heroImage は ../../../ のまま)  →  直後: / 500  (パスが実際に誤っているので当然)
heroImage を ../../ に修正                                     →  4秒後: / 200  /blog/ 200  /blog/x/ 200
heroImage をタイプミス → 直す                                   →  4秒後: 200 に回復

移動した直後は 500 になりますが、パスを直せば4秒以内に全ページ 200 に戻り、対応表にも古い項目は残りません。以下は、なぜ壊れていたのか・なぜ 7 系にしたのか・更新で何が変わり何が変わっていないか、の詳細です。それぞれの調査と判断のチャットは、付録の「Claude Code とのやりとり」に載せています。

原因: 画像の対応表が、消えないまま積み上がる

Astro は dev サーバーの動作中、.astro/content-assets.mjs というファイルを生成しています。「記事のどのファイルが、どの画像をどのパスでインポートしているか」の対応表です。

壊れた状態のこのファイルを開くと、同じヒーロー画像の項目が3世代ぶん並んでいました。

// .astro/content-assets.mjs (壊れた状態・該当3行)
import __ASTRO_IMAGE_IMPORT_25DjiK from "../../../assets/posts/synapusyu-summer-piano/hero.jpg?astroContentImageFlag=&importer=src%2Fcontent%2Fposts%2F_drafts%2Fsynapusyu-summer-piano.mdx";
import __ASTRO_IMAGE_IMPORT_2aaJc6 from "../../../assets/posts/synapusyu-summer-piano/hero.jpg?astroContentImageFlag=&importer=src%2Fcontent%2Fposts%2Fsynapusyu-summer-piano.mdx";
import __ASTRO_IMAGE_IMPORT_erlu4  from "../../assets/posts/synapusyu-summer-piano/hero.jpg?astroContentImageFlag=&importer=src%2Fcontent%2Fposts%2Fsynapusyu-summer-piano.mdx";

importer= 以降がインポート元の記事の位置、その前が相対パスです。読み解くと次の3つになります。

相対パスインポート元として記録されている記事の位置解決できるか
../../../assets/…/hero.jpgposts/_drafts/synapusyu-summer-piano.mdxできる (移動前の正しい組み合わせ)
../../../assets/…/hero.jpgposts/synapusyu-summer-piano.mdxできない
../../assets/…/hero.jpgposts/synapusyu-summer-piano.mdxできる (移動後の正しい組み合わせ)

2番目が原因でした。画像の解決はインポート元のディレクトリを起点に行われるので、src/content/posts/ から3つ上へ遡ると src/ の外 (リポジトリ直下) に出てしまいます。そこに assets/ はありません。

この組み合わせが生まれるのは、公開作業が2段階だからです。

  1. git mv で記事を _drafts/ から1階層上へ移動する (この時点で frontmatter のパスはまだ ../../../ のまま)
  2. frontmatter のヒーロー画像のパスを ../../ に書き換える

この2つの操作の間、「新しいファイル位置 × 古い相対パス」という解決できない組み合わせが実在します。実際の公開作業では、この2つはほぼ同時でした。対応表の書き出しは、最後の変更から 0.5 秒待って行われます。書き出された 12:51:34.0 の時点で修正後のパスがすでに入っていて、移動したファイルの更新時刻が 12:51:33.5 だったことから、2つの操作の間隔は1秒足らずと見ています。この短い間でも dev サーバーのファイル監視は途中の状態を拾い、対応表に書き込みます。ここまでは仕組みどおりの動きです。

問題は、手順 2 のあとも、対応表の2番目の項目が消えないことでした。Astro 5.18.1 のソースを読むと、この対応表を保持している集合には追加しかありません。

// packages/astro/src/content/mutable-data-store.ts (astro@5.18.1)
#assetImports = new Set<string>();          // 35行目

addAssetImport(assetImport: string, filePath?: string) {
  const id = imageSrcToImportId(assetImport, filePath);
  if (id) {
    this.#assetImports.add(id);              // 66行目
    this.#writeAssetsImportsDebounced();
  }
}

add はありますが、deleteclear もファイル中に一度も出てきません。記事ファイルの移動やパスの修正でコレクションのエントリが削除・更新されても、そのエントリが以前に登録した画像の項目は集合に残り続けます。

この対応表はコレクションの画像を扱う全ページが共有しているため、1件でも解決できない項目があると、対応表そのものの読み込みが失敗して、参照しているページがまとめて 500 になります。記事一覧もトップページも巻き添えになるのはこのためでした。

再起動で直る理由も同じ場所にあります。起動時はこの集合が空から始まり、ファイル監視が実在するファイルを順に読んで登録し直します。コレクションのエントリ (こちらは前回から持ち越されます) も、ファイルが消えているものは起動時に整理されるので、対応表は現在の状態を反映したものになります。記事ファイルを touch しても復旧しないのは、集合そのものは作り直されないからでした。

上流ではすでに直っていた

原因が分かったところで Astro のリポジトリを検索すると、同じ症状の報告が3件 (#13149#13586#16097) 見つかり、いずれもすでに閉じられていました。#16097 を閉じた修正が「コンテンツの更新時に古い画像インポートを消す」というもので、書き出しの前に集合を作り直す実装に変わっています。

// 修正後 (astro@7.2.2)
#rebuildAssetImports() {
  this.#assetImports.clear();
  for (const collection of this._collections.values()) {
    for (const entry of collection.values()) {
      // 現在のエントリから登録し直す
    }
  }
}

npm で公開されているファイルを境目の前後のバージョンで確認したところ、#assetImports.clear( を含むのは 6.2.0 以降で、6.1.0・6.0.8・5.18.2 にはありませんでした。5 系への取り込みもありません。

修正のソースコードには、症状を説明したコメントがそのまま残っています。

This ensures stale import IDs are removed when entries are updated or deleted,
preventing unrecoverable ImageNotFound errors in astro dev after a content
entry's image path is temporarily set to an invalid value and then restored.

「画像パスが一時的に無効な値になり、その後もとに戻された」。今回の公開作業そのものです。

なぜ 7 系にしたか

6.2 以降なら修正が入っています (6.4.8 と 7.2.2 は、実際に dev が回復することを確認しました)。1メジャーずつ上げるほうが何かあったときに切り分けやすいのですが、リリース日を並べると、6 系を経由する理由がありませんでした。

バージョンリリース日
6.0.02026-03-10
6.2.0 (修正が入ったバージョン)2026-04-30
6.4.8 (6 系の最終)2026-06-17
7.0.02026-06-22
7.2.2 (今回入れたバージョン)2026-08-13
7.2.10 (執筆時点の最新)2026-08-31

6 系の最後のパッチは 7.0.0 の5日前で、それ以降 (この記事を書いている 2026-09 時点で約2か月半) 1件も出ていません。5 系は 6.0.0 のあとも 5.18.2 (2026-05-26) まで散発的に出ていたので「旧メジャーに一切パッチが出ない」わけではありませんが、6 系については現時点でゼロです。いま 6 に上げても、次に何か起きれば結局 7 に上げることになるでしょう。7 系のほうは、7.0.0 以降も週に2〜3本のペースでパッチが続いています。

7 系の未解決の報告のうち、この構成に関係しそうな2件 (動的な記事ページのホットリロードが効かないUnicode の記号 (約物) が化ける) は、手元で試していずれも再現しませんでした (手元の環境は WSL・npm・アダプタなし)。後者はタイトルに Windows 限定と書かれているものです。挙動が変わっていたのは astro preview の起動方法で、これは「更新後に挙動が変わったこと」に書いています。ブログ主の考えは最初から「最新にしておけばよく、7 で問題がなさそうなら 7」でした。この結果を受けて、ブログ主の判断で、作業時点の最新だった 7.2.2 まで一度に上げました。

移行で対応が必要だったこと

更新そのものは小さな作業でした。設定ファイルの置き場所が変わったのでリネームし (src/content/config.tssrc/content.config.ts)、依存を上げました。変更点の一覧は公式の v6v7 のアップグレードガイドにあります。それ以外に、2つ対応が要りました。

MDX インテグレーションのメジャーを本体に揃える (気づきにくい)

本体 6.4 系に1メジャー古い MDX インテグレーション (@astrojs/mdx の 5 系) を組み合わせて試したとき、ビルドは何事もなく成功するのに、Markdown のテーブルがすべてただの文字列になりました。表のある9記事・19個の表が | 見出し | 見出し | という素のパイプ記号の段落になっていました。インテグレーションを本体と同じメジャーに揃えると表は戻りました (最終的な組み合わせは本体 7.2.2 とインテグレーション 7.0.5)。

ビルドが通ってしまうので、出力の突き合わせをしていなければ、そのまま公開していたところでした。

Markdown パイプラインの互換指定

Astro 7 から、Markdown/MDX の描画が Astro 純正のパイプライン (Sätteri) に切り替わり、従来の remark/rehype は既定では入らなくなりました。このブログは外部リンクへの rel 付与や、本文中に表示している文字数の案内の生成を自作の rehype プラグインでやっているので、それらが動かなくなります。こちらはビルドが落ちるので気づけます。公式に用意されている互換の指定を足して解決しました。

// astro.config.mjs
import { unified } from '@astrojs/markdown-remark';

export default defineConfig({
  markdown: {
    processor: unified({ rehypePlugins: [rehypeExternalLinks, rehypeArticleInfo] }),
  },
});

これは移行のための互換の指定なので、いずれ純正パイプラインへ移植するか、このまま使い続けるかを決める必要があります。急ぎではないので、別の課題として切り出しました。

更新後に挙動が変わったこと

記事の見た目とは別に、運用に関わる変化がありました。

画像が拡大されなくなりました。 Astro 6 以降、既定の画像サービスは元画像より大きいサイズを要求されても拡大しません。このブログは OGP 用の画像をヒーロー画像から幅 1200px で生成しているのですが、ヒーロー画像が 1200px より狭いと、生成物は元の幅のままで OGP の meta タグ (og:image:width) は 1200 を名乗る、というズレが出ます。ヒーロー画像が幅 900px のドラフト記事で実測したところ、実物 900x1600 に対して meta は 1200x2133 でした。要求幅を元画像の幅で頭打ちにして meta と揃えました。あわせて、公開前に気づけるよう、ヒーロー画像が 1200px 未満なら警告を出すチェックを追加しています。

dev サーバーが単一起動管理になりました。 Astro 7.2 から、astro dev はロックファイルを持ち、同じプロジェクトで2本目を起動しようとすると、次のように拒否します。

Dev server already running at http://localhost:4321 (pid 4193)
  Stop:   astro dev stop
  Status: astro dev status
  Logs:   astro dev logs

astro dev stop / status / logs という管理コマンドもつきました (astro preview のほうは、コマンドがすぐ戻ってサーバーだけが残るバックグラウンド起動に変わっています)。

このブログは「レビュー用の 4321 はブログ主専用、AI は 4322 以降」という運用にしているので、この変更は AI 側の起動手順に直接関わります。astro devAI エージェントの実行環境を自動検出してバックグラウンド起動を強制するのですが、バックグラウンド起動と「ロックを無視する」フラグは同時に指定できないため、そのままでは並走できません。

# ロックを無視して前景で立てる (自動検出を無効にする環境変数が必要)
ASTRO_DEV_BACKGROUND=1 npx astro dev --port 4322 --ignore-lock

この形ならブログ主の 4321 のロックに触れないので、astro dev status から見える dev サーバーは 4321 のままになります。逆に AI 側がロックを取ってしまうと、ブログ主が 4321 を起動できなくなります。

止めるときにも注意が要りました。まず、Astro のバージョンとは関係のない話として、pkill -f 'astro dev' はポートに関係なくすべての astro dev に当たるので、AI が自分の 4322 を止めるつもりで実行すると、ブログ主の 4321 も一緒に止まります (更新後に一度、実際にこれで止まりました)。npx astro dev stop--port を見ておらず、ロックファイルに登録されたサーバー、つまり 4321 を止めます。起動側でも、既存の dev を置き換える --force は同じ結果になります。このブログでは、ポートから pid を引いてその1本を止める小さなスクリプトを用意し、4321 を指定したら拒否するようにしました。

変わっていないことを、どう確かめたか

メジャーバージョンを2つ上げているので、「直った」ことよりも「他が変わっていない」ことの確認に手間をかけました。公開済みの記事が当時13本あり、目視では見落とすおそれがあるためです。

1. 出力の突き合わせ。 更新前と更新後で本番ビルドを作り、生成された HTML を比較しました。テーブル・画像・外部リンク・見出し・補足ボックスの数も、記事ごとに表示している文字数も一致しています。差分は3種類で、いずれも表示に影響しないものでした (既定値と同じ属性が出力されなくなった・CSS ファイル名が変わった・URL 中の &&amp; に正しくエスケープされるようになった)。前述の MDX のテーブル消失は、6 系で試したときに同じ突き合わせで見つかったものです。

2. ピクセル比較。 Playwright にデスクトップ (1280px) とモバイル (390px) のページ全体のスクリーンショットを撮らせて、更新前後で1ピクセルずつ比較しました。見つかった見た目の差は1点でした。フッターのアイコンとサイト名の間隔が 4px 詰まっていました。Astro 7 で HTML の空白の扱いが変わり、要素の間にあった空白1つが取り除かれたためです。もともと CSS の余白と HTML の空白の合計で見えていた間隔だったので、CSS 側に寄せて元に戻しました。

生成された画像も比較しました。291件のうち31件がバイト単位で違っていましたが、エンコーダの違いによるものと見ていて、切り取り位置や大きさは変わっていません。位置をずらしながら重ねても最も一致するのはずらさない位置で、ぼかしをかけると差がほぼ消えることから、高周波成分だけの差と判断しています。

更新後、ブログ主も本番の記事を数本目視して、気になる点はなかったとのことでした。

まとめ

  • dev サーバーが ImageNotFound から戻らなくなるのは、画像インポートの対応表が古い項目を消さないため。1件でも解決できない項目があると、参照するページがまとめて 500 になる
  • 公開作業は「ファイルを移動する」「パスを直す」の2段階なので、その間の一瞬に解決できない組み合わせが生まれ、毎回この状態を通る
  • この不具合は Astro 6.2.0 (2026-04-30) で修正済み。ヒーロー画像の差し替えやパスのタイプミスで壊れるのも同じ仕組みなので、更新でまとめて解消される
  • 今回はメジャーを2つ上げたので、「直ったこと」より「他が変わっていないこと」の確認に手間をかけた。ビルドが通る不具合 (テーブルの消失) は、出力の突き合わせで見つかった

ブログ主は「公開したら dev を再起動する」運用で2週間回していました。公開フローを変える案 (Claude Code が当時提案していたもの) もありました。修正は4月末にリリース済みだったので、バージョンを上げることで解消しました。同じ ImageNotFound が dev だけで出て、再起動すると直り、しばらくするとまた出る、という場合は、Astro 6.2 より前なら更新で解消します。

付録: Claude Code とのやりとり

最初の依頼は「調べて」でした。壊れた dev サーバーが残っていたので、その状態のまま調査に入っています。

👤 ブログ主のプロンプト
ちょうどいま別の記事の公開作業をしましたが、ローカルホストの4321が止まりました。画面に表示されたターミナルと画面表示をメモしたので、コピペしておきます。この情報も含めて、現状なにが起こっているのか、確認おねがいします。4321は再起動すれば動きだすので、今問題というよりも、こういう問題がおこるので、今後公開作業のときに発生しないようにしたいということです。ちなみに、公開作業じゃなく、更新作業のときでも4321が壊れる現象が発生するときがあります。作業はいいので、まずは調査おねがいします。
🤖 Claude Code
壊れたままの dev サーバーの .astro/content-assets.mjs を調べ、同じヒーロー画像の項目が3世代並んでいて、移動直後 (パス修正前) の組み合わせが解決できないまま残っていることを報告。Astro 5.18.1 のソースではこの対応表を持つ集合に追加しかなく、一度入った壊れた組み合わせは再起動まで消えない。条件は「(heroImage のパス, mdx の位置) の組み合わせが一度でも解決不能な状態で保存されること」なので、公開移動のほかに画像の差し替えやパスのタイプミスでも起きる。上流に同型の報告が3件あり、修正は 6.2.0 に入っていた。対策は「公開フローを変える (ファイルを移動せず、frontmatter のフラグで公開扱いにする案)」と「Astro を更新する」の2本立てで、前者は公開のたびに壊れる経路だけを消し、後者はこの壊れ方を仕組みごとなくす、と整理した。移動からパス修正までの間隔は、この時点では数十秒と見ていた。
👤 ブログ主のプロンプト
Astroのバージョンアップですが、どのみち必要なので、やりたいです。まずこれからかなと思ってます。今、draftのブログがいくつかあるのと、すでに公開済みのものがあります。数は10〜20個なので、何か問題があってもあなたで修正できる範囲かなとは思ってます。ということで、バージョンアップのリスク等検討おねがいします!
🤖 Claude Code
隔離した worktree で 5.18.1 → 6.4.8 → 7.2.2 を実際にビルドして、出力を突き合わせた結果を報告。6.4.8 に @astrojs/mdx 5 を組み合わせるとビルドは通るのに Markdown のテーブルがすべて崩れ (公開9記事・19個)、@astrojs/mdx も 6 に揃えると出力はほぼ同一。7.2.2 は設定そのままだとビルドが落ち、@astrojs/markdown-remarkprocessor: unified() を足せば通る。6.4.8 の dev で公開移動を再現し、パスを直すと3秒以内に全ページ 200 へ自動回復することも確認した。推奨は「まず 6.4.8 に上げて本題を解決し、7 は落ち着いてから」。6 は設定変更が実質リネーム1件なのに対し、7 は Markdown パイプラインの切り替えという判断を含むため。
👤 ブログ主のプロンプト
なるほど、ver7の安定性はどうですかね?最新にしておけばいいのでは、と思っています。7で問題なさそうなら7がいいのかなと思っていますが、まだ安定してないとかバグが多いとかであれば6もありかなと。どうでしょうかね?
🤖 Claude Code
リリース日を並べると、6 系の最終 6.4.8 は 7.0.0 の5日前で、以降 6 系のパッチは1件も出ていない。この時点で 6 に上げても、次に何か起きれば結局 7 に上げることになる。7.0.0 以降8週間の issue 作成数は 180件で、6.0.0 直後の同期間 (254件) より少なく、regression ラベルの未解決は 0件。この構成に当たりうる未解決の報告を手元で試し、動的な記事ページのホットリロードと Unicode の記号 (約物) の件はどちらも再現しなかった。挙動の変化として astro preview のバックグラウンド起動だけは手元でも出た (指示ファイルの更新で済む)。6 を選ぶ理由がなくなったので、推奨を 7 に変えた。
👤 ブログ主のプロンプト
はい、では実作業にうつりましょう。今作業しているドラフトや公開済みには影響ないでいいんですよね?あと、README.mdとかにルールを書いたりしてますけど、それが変わったりしますかね?画像のパス系統。
🤖 Claude Code
記事の mdx とアセットは1件も変更しないこと、ヒーロー画像の相対パスの決まり (_drafts/../../../posts/ 直下は ../../) はディレクトリの深さから来るもので Astro のバージョンとは無関係なので変わらないこと、の2点を回答。変わるのは設定ファイルの置き場所と、Claude Code 向けの指示ファイルにあった「公開後は dev を再起動する」という暫定運用を消すこと。そのうえで作業に入った。更新後は前後のビルド出力を突き合わせて表・画像・リンク・見出しの数が一致することを確認し、ピクセル比較で見つかった差 (フッターのサイト名が 4px 左に寄る) を CSS 側で補正した。残課題として、ヒーロー画像の幅チェック (1200px 未満の警告) が未対応と挙げた。
👤 ブログ主のプロンプト
PCを再起動して、4321を起動しました。起動時にwarnが出てますね。大丈夫でしょうか?あとは、本番は数記事みましたが、ざっと見は特に気になるところはありませんでした。hero画像チェックは何か今回のために変更があったのですかね?もとからあった問題だったら本issueでは取り扱わないですが、どうしましょうか?
🤖 Claude Code
警告は rehypePluginsmdx() から unified() へ移すよう案内する非推奨の通知で、動作には影響していない。案内どおり移して、出力が同一であることを確認した。残課題に挙げていたヒーロー画像の幅の件は、今回の更新で新しく生じた問題。Astro 5 は足りない分を拡大して 1200px の OGP 用の画像を出していたのが、Astro 6 以降は拡大しなくなったためで、ドラフトの1本 (幅 900px) が該当する。この issue で扱うのが妥当と答え、あわせて直した。残る宿題として、互換の指定 (unified()) を使い続けるか Astro 純正の Sätteri に移植するかは「急ぎではないので、Astro 8 のタイミングで再検討がよさそう」と添えた。
👤 ブログ主のプロンプト
「unified() から Sätteri への移植判断 (急ぎではないので、Astro 8 のタイミングで再検討がよさそうです)」→ 永久に忘れてしまいそうなので、別issueにするなどしておいてもらえますか?余裕があるときにやっておけばいいのですよね?フラグ式は不要でいいです。必須じゃなくなったので。
🤖 Claude Code
別 issue として起票した (優先度は低め)。時間がたってから開いても再開できるように、なぜ互換の指定を使っているか・対象プラグイン2本の移植論点・完了条件 (移植するなら出力が変わらないことを今回の出力を基準に照合) を本文に入れ、目安は Astro 8 が出るころ (メジャー間隔が約3.5か月なので 2026-10 ごろ) とした。フラグ式公開は不採用として記録し、公開フローは従来どおり (git mv と heroImage のパス書き換え)。

並行して別の記事を書いていた Claude Code のセッションから、dev サーバーが立てられないという報告が届きました。

👤 ブログ主のプロンプト

別のセッションからブログを更新しようとしたら上手くいかなかったようで、以下コメントをもらっています。確認おねがいします。

Astro 7.2 で AI の dev 起動手順が変わっています。dev サーバーがシングルトン管理になり、4321 が起動中だと AI の npm run dev は「already running」で拒否されます。さらに AI エージェント環境の自動検出でバックグラウンド起動が強制され --ignore-lock と衝突するため、今回は検出用の環境変数を外して npx astro dev --ignore-lock で 4322 に立てました。

🤖 Claude Code
報告どおりだった。素の npm run devDev server already running で拒否され、--ignore-lock だけだと「AI エージェント環境の自動検出と併用できない」というエラーになる。ソースを読むと ASTRO_DEV_BACKGROUND が設定されていれば自動検出そのものが無効になるので、検出用の環境変数を外すより簡単な起動方法として ASTRO_DEV_BACKGROUND=1 npx astro dev --port 4322 --ignore-lock を指示ファイルに書き、--force は 4321 を止めてしまうので使わないことも明記した。あわせて訂正が1件。最初の報告で移動からパス修正までを「数十秒」と見ていたのは誤りで、対応表の更新時刻から見ると間隔は1秒足らず。2段階の操作である以上、どれだけ速く直しても途中の状態はファイル監視に拾われる、というのが正しい理解だった。

更新から2週間ほどたって、レビュー用の 4321 がまた止まりました。

👤 ブログ主のプロンプト

本日ですが、4321が止まりました。これまでのローカルでのブログ更新で止まる場合が何度かあります。確認いただけると助かります。以下のような表示で止まります。

11:25:45 [200] /blog/_drafts/shiminzei-nofusho/ 51ms

[プロセスはコード 15 (0x0000000f) で終了しました]

🤖 Claude Code
終了コード 15 は SIGTERM なので、誰かが止めたことになる。セッションの記録を追うと、時刻のつじつまが合った。ログの最終行が 11:25:45、別の Claude Code セッションが pkill -f '[a]stro dev' を実行したのが 11:25:58 で、このコマンドはポートに関係なくすべての astro dev に当たる。もう1つ、npx astro dev stop--port を見ておらず、ロックファイルに登録されたサーバー、つまりブログ主の 4321 を止める。根本は、更新のときに AI 用の dev の起動手順は書いたのに停止手順を書いていなかったことで、各セッションが独自に pkill を組み立てていた。ポートから pid を引いてその1本を止めるスクリプトを追加し、4321 を指定したら拒否するようにした。

依頼が「調査」と「実作業」に分かれていて、その間にバージョンの選択と影響範囲の確認が挟まっている、という流れでした。更新から2週間後の再発は症状が似ていましたが、原因は別でした。

参考リンク

公式・一次情報

関連する自サイトの記事

Astro のバージョンアップを経験された方の記事