Terminal.appが起動しなくなった
(SSH越しのCodexでsavedState破損)

関連記事

1. 現象

macOS標準のTerminal.appが起動直後にクラッシュし、com.apple.Terminal.savedStateを削除して復旧しました。

Terminal.appを起動すると、約0.85秒でプロセスが落ちる状態が続いていました。
シェルのエラーではなく、Terminal.appそのものがクラッシュしています。

procLaunch: 2026-06-07 18:40:55.1765 +0900
timestamp:  2026-06-07 18:40:56.00 +0900Code language: CSS (css)

~/Library/Saved Application State/com.apple.Terminal.savedStateを削除したところ、起動できるようになりました。

1.1. クラッシュ発生時の確認手順

クラッシュレポートが.ipsで残っているかをまず確認します。

ls -lt ~/Library/Logs/DiagnosticReports/Terminal-*.ips | headCode language: Bash (bash)

複数件あれば、タイムスタンプとprocLaunchの差を見て、起動直後のクラッシュか通常使用中のクラッシュかを区別します。
前者であればsavedStateが疑わしく、後者が先に複数回起きていれば、savedStateを壊したきっかけを別途探すことになります。

1.2. .ipsファイルを読む

macOS 14以降、クラッシュレポートは.crashではなく.ips形式で保存されます1
保存先は~/Library/Logs/DiagnosticReports/で、Terminal-*.ipsとして残ります。

.ipsファイルは1ファイル内に2つのJSONが並んでいる構造になっています2
先頭行に概要JSON、続いて詳細JSONが入っており、jqでそのまま処理すると「Cannot index null with null」というエラーが出ます。
select(.procName == "Terminal")を挟むと詳細JSON側だけを対象にできます3

jq -r '
  select(.procName == "Terminal") |
  "procLaunch: " + .procLaunch,
  "exception: " + .exception.type + " " + .exception.subtype,
  "faultingThread: " + (.faultingThread|tostring),
  (.threads[.faultingThread].frames[0:8][]? |
    "  \(.imageIndex) offset=\(.imageOffset) \(.symbol // "")")
' ~/Library/Logs/DiagnosticReports/Terminal-*.ipsCode language: Bash (bash)

2. 対処法

2.1. savedState削除

rm -rf ~/Library/Saved\ Application\ State/com.apple.Terminal.savedStateCode language: Bash (bash)

削除したら、Terminalが起動できました。
次回起動すれば、また空のsavedStateが再生成されます。

3. ログから見えた時系列

今回は同日に8件の.ipsが残っていました。
整理すると、3つの段階に分かれます。

3.1. 第1段階:通常使用中のクラッシュ(14:56〜16:32)

timestampprocLaunch例外faultingThread
14:56:09前日 14:46:44EXC_CRASH / SIGABRT0
16:32:4814:55:52EXC_BAD_ACCESS / SIGSEGV0

14:56のレポートには、クラッシュ直前のログに次の出力がありました。

malloc: Heap corruption detected, free list is damaged
malloc: Incorrect guard valueCode language: HTTP (http)

これはTerminal内部のヒープ領域が壊れていたことを示しています。
ヒープはアプリが使う動的メモリ領域のことで、その管理情報が破損した状態です4
設定ミスとは性質が異なります。

16:32のクラッシュではlibFontParser.dylibCoreTextCoreGraphicsが関与しており、フォント解析と文字描画の処理でクラッシュが発生していました5

3.2. 第2段階:保存状態の破損

16:32以降にTerminalが終了したとき、その時点の壊れた状態がsavedStateに書き込まれました6
restorecount.plistの内容は次のとおりです。

count: 5
start: <クラッシュ直前の起動時刻に一致するタイムスタンプ>
end:   0Code language: HTTP (http)

end: 0は復元処理が正常終了していないことを示します7
windows.plistには、復元対象としてproject-name — zshというウィンドウタイトルが残っていました。

3.3. 第3段階:起動時の連続クラッシュ(18:38〜18:40)

timestampprocLaunch例外アドレスfaultingThread
18:38:5718:38:550x000000000000001810
18:38:5816:32:530x00002020202020400
18:39:1018:39:080x00005fffffffffef0
18:39:1618:39:140x00000000000000203
18:39:2518:39:230x00007f8dfc4fffef0
18:40:5618:40:550x00005fffffffffef0

このうち18:39:10、18:39:25、18:40:56の3件は、先頭8フレームが完全に一致していました。

libsystem_platform.dylib  offset=6185
Terminal                   offset=166968
Terminal                   offset=166741
Terminal                   offset=211393
Terminal                   offset=338984
Terminal                   offset=331207
AppKit                     offset=294854
AppKit                     offset=292935

faultingThreadはすべて0、つまりメインスレッドです8
いずれも起動から数秒以内に落ちており、savedStateを読むたびに同じ復元処理が同じ地点で失敗していたことがわかります9

統合ログを見ると、クラッシュ直前にCFURLResolveBookmarkDataが呼ばれていました。
これはCoreFoundationが保存済みのパス参照を実際のファイルシステム上のパスに解決する処理です10
savedState内の復元対象パスを読もうとした直後に落ちていたことが読み取れます。

4. SSH越しのCodex使用との関係

この期間、SSH経由でCodexを使用していました。

ローカルのTerminal.appには、リモートのCodexから次のような出力が大量に流れます。

  • ANSIエスケープシーケンス(色指定、カーソル移動、画面消去などの制御文字)11
  • 幅の異なるUnicode文字、罫線文字、絵文字
  • 対話UIの頻繁な画面再描画

クラッシュアドレスのひとつ0x0000202020202040は特徴的です。
0x20はASCIIの空白文字で、アドレス全体が空白文字の繰り返しに近い値になっています12
空白を含む表示バッファや文字列データを、ポインタとして参照してしまった可能性が連想されます。
断定はできませんが、端末出力由来の破損データという仮説とは整合します。

14:56のヒープ破損と16:32のフォント系クラッシュを起点に、Terminal内部の状態が徐々に壊れていき、その状態がsavedStateに書き込まれました。
次回起動時に復元しようとして連続クラッシュが起きた、というのがログから読み取れる流れです。

公開情報を確認した範囲では、「Codex + SSH + macOS Terminal savedStateクラッシュ」という組み合わせの一致事例は見つかりませんでした。
ただし今回のログ上、SSH越しのCodexが引き金だった可能性は十分にあります。

4.1. 端末エミュレータの選択

SSH越しにCodexのような重いTUIツールを使う場合、Terminal.appではなくiTerm2、Ghostty、WezTermなど別のエミュレータを検討する価値があります13

savedStateの実装やメモリ管理の方針はアプリごとに異なるため、同じ状況でも挙動が変わります。

5. まとめ

今回の連続クラッシュは3段階の連鎖でした。
SSH越しのCodex使用中に描画処理でヒープ破損が発生し、その状態がsavedStateに書き込まれ、次回起動時に復元処理が同じ地点で繰り返しクラッシュしていました。
直接の解決はsavedState削除でしたが、ログを追うことで「なぜ壊れたか」までかなり絞ることができました。

Terminal.appがクラッシュしたとき、コマンドを疑う前に.ipsのタイムスタンプとprocLaunchの差を確認する、というのは覚えておいて損のない手順です。

  1. .ips形式はiOS 15とmacOS 12(Monterey)で導入されました。内部はJSONで、クラッシュのメタデータと詳細情報を含みます。Appleの公式コンソールドキュメントにも「クラッシュレポートのファイル名は.ips拡張子を持つ」と明記されています。 – View reports in Console on Mac – Apple Support
  2. 先頭行の概要JSONにはアプリ名・タイムスタンプ・OSバージョン・incident_idなどが入り、続く詳細JSONにスレッド情報・スタックトレース・usedImagesが含まれます。Appleの開発者ドキュメント「Interpreting the JSON format of a crash report」にこの構造の詳細が記載されています。 – Interpreting the JSON format of a crash report | Apple Developer Documentation
  3. jqはコマンドラインで動作する軽量なJSONプロセッサです。sedやgrepのJSON版にあたり、フィルタ・変換・整形をパイプで組み合わせて実行できます。macOSにはプリインストールされていないため、Homebrewでbrew install jqとして導入します。 – jqlang/jq | GitHub
  4. ヒープ破損(heap corruption)は、アプリが確保・解放したメモリ領域の管理テーブルが壊れた状態を指します。原因としては、バッファオーバーフロー、解放済みメモリへのアクセス(use-after-free)、初期化されていないポインタの参照などが典型的です。mallocが検出したガード値の不一致は、隣接するメモリブロックの境界が壊れていることを意味します。 – Identify Heap Corruption | Educative
  5. CoreTextはテキストのレイアウト・組版・描画を担うAppleのフレームワークです。CoreGraphicsはQuartz 2Dグラフィックスエンジンへのインターフェースで、低レベルの描画処理を担います。libFontParser.dylibはフォントファイルの解析に使われる内部ライブラリです。これらが同時にスタックに現れる場合、文字表示や画面描画の処理中にクラッシュが起きていることを示します。 – Core Text | Apple Developer Documentation
  6. macOSのsavedStateは、AppKit.frameworkとtalagentという常駐エージェントによって管理されます。TALはTransparent App Lifecycleの略で、「アプリが起動しているかどうかをユーザーが意識しなくて済む」というコンセプトに由来する機能名です。System Settings > Desktop & Dock > 「アプリケーション終了時にウィンドウを閉じる」がオフのとき、終了前の状態がsavedStateに書き込まれ、次回起動時に復元されます。 – Saved Application State | Archaeology
  7. restorecount.plistは各アプリのsavedState復元の試行回数と開始・終了タイムスタンプを記録するファイルです。startにはmacOSの基準時刻(Mach absolute time)が入り、endが0のままであれば復元処理が完了せずにアプリが終了したことを意味します。 – Saved Application State | Archaeology
  8. faultingThreadはクラッシュが発生したスレッドの番号を示します。0はメインスレッドを指し、UIの描画・イベント処理・ウィンドウ復元など、アプリの主要な処理が実行されるスレッドです。Appleの「Interpreting the JSON format of a crash report」には、faultingThreadフィールドの意味が説明されています。 – Interpreting the JSON format of a crash report | Apple Developer Documentation
  9. クラッシュログのスタックトレースにおいて、複数のレポートで先頭フレームのimageIndexとimageOffsetが一致する場合、同じコードパスで繰り返しクラッシュしていることを意味します。Apple Developerのドキュメントでは、このような一致をバグの再現性の指標として扱っています。 – Analyzing a crash report | Apple Developer Documentation
  10. CFURLCreateByResolvingBookmarkDataはCoreFoundationのAPI関数で、アプリが以前に保存したブックマークデータ(ファイルシステム上のファイル・フォルダへの永続的な参照)を、現在のパスに解決して返します。savedState内にウィンドウの作業ディレクトリや開いていたファイルへの参照が含まれる場合、このAPIが復元処理中に呼ばれます。 – CFURLCreateByResolvingBookmarkData | Apple Developer Documentation
  11. ANSIエスケープシーケンスは、テキストの色・スタイル・カーソル位置などを制御するための特殊文字列です。ESC文字(\x1b)と[で始まり、続くパラメータと終端文字でコマンドを指定します。1970年代に標準化され、現代のほぼすべてのターミナルエミュレータが実装しています。Codexのような対話型CLIツールは、画面描画のためにこれらを大量に出力します。 – ANSI escape code | Wikipedia
  12. ASCIIコード0x20は半角スペース(U+0020)を表します。0x0000202020202040というアドレスは、0x20が連続するパターンを含んでおり、空白文字で埋まったバッファや文字列データをポインタとして誤参照した可能性を示唆します。ただし、実際の原因を確定するにはシンボル情報が必要です。 – ANSI escape code | Wikipedia
  13. iTerm2はmacOS向けの高機能エミュレータで、tmuxとのネイティブ統合やPythonスクリプトAPIを持ちます。GhosttyはMetalを使ったGPUレンダリングでmacOSネイティブな動作が特徴です。WezTermはRust製でLua設定とクロスプラットフォーム対応が強みです。いずれもTerminal.appとは異なるsavedState実装を持たない、またはAppleのsavedState機構を使わない設計になっています。 – Best Terminal for Mac in 2026 | DEV Community