障害ログを渡したら、93秒でファイル・行番号・修正案まで返ってきた——実物
【報告】supportアプリの調子がおかしいです。 「別の一言」ボタンを押したり、日記を入力したりすると、右上の保存表示(「保存中」→「保存済み」)が出なくなりました。データが保存されているのか不安です。 ブラウザのコンソールに以下のエラーが出ています。 ---- console ---- Uncaught TypeError: Cannot set properties of null (setting 'textContent') at queueSave (http://localhost:4180/app.js:732:29) at HTMLButtonElement.<anonymous> (http://localhost:4180/app.js:456:5) ----------------- 環境: Mac / Chrome / localhost:4180 で起動(app_broken フォルダ一式)
AIの出力(原文ママ・93秒後)
- 診断対象:
app_broken/(app.js / index.html / styles.css)+エラーログ.txt - 症状: 「別の一言」ボタンや日記入力で、右上の保存表示(「保存中」→「保存済み」)が出ない
- コンソール:
Uncaught TypeError: Cannot set properties of null (setting 'textContent') at queueSave (app.js:732:29)
1. 根本原因
app.js 378行目の querySelector のセレクタ文字列が、HTML側の実際の id と1語ずれている。
| 場所 | 記述 | 判定 |
|---|---|---|
| app.js 378行目 | saveState: document.querySelector("#saveStatus"), | 誤り(唯一の不一致) |
| index.html 63行目 | <span class="save-state" id="saveState">保存済み</span> | 実際の id は saveState |
| styles.css 299行目 | .save-state { ... } | class基準なので表示崩れなし |
なぜこのエラーになるか
- HTML に
id="saveStatus"の要素は存在しない(フォルダ全体を検索してsaveStatusの出現は app.js:378 の1箇所のみ)。 - そのため
document.querySelector("#saveStatus")は null を返し、els.saveStateが null になる。 - 保存表示を更新する
queueSave()の先頭(732行目29桁)でels.saveState.textContent = "保存中"を実行した瞬間、null にプロパティを設定しようとしてTypeError: Cannot set properties of null (setting 'textContent')が発生する。 - スタックの
HTMLButtonElement.<anonymous> (app.js:456:5)は「別の一言」ボタンのクリックハンドラ(450〜457行目)からの呼び出しで、ログの再現手順と一致する。
このコードベースは「els のプロパティ名 = HTML の id」という命名規約で統一されており(例: progressRate↔#progressRate、syncState↔#syncState)、#saveStatus だけが規約から外れた typo。
2. 影響範囲
(a) queueSave() 経由 — 全30箇所が失敗。データが保存されない(実害あり)
queueSave() は1行目(732行目)で即例外になるため、その先の setTimeout が予約されず saveState()(localStorage保存)が一度も実行されない。さらに進捗・週チャート・週次ふりかえり・カレンダーの再描画、クラウド同期(queueCloudSync)・Google同期(queueGoogleSync)もすべてスキップされる。
該当する操作(いずれも変更はメモリ上だけで、リロードすると消える):
- 今日の意識(intention)の入力(443行目〜)
- 「別の一言」ボタン(450行目〜 ← ログの再現操作)
- 日記の入力(459行目〜)
- 気分・元気スライダー(467・476行目〜)
- 週次ふりかえりの入力(573行目〜)
- タスクのチェック/タイトル編集/エリア変更/削除(1985〜2013行目)
- 予定の時刻・タイトル変更/削除(2121〜2143行目)ほか、リスト系の編集・削除全般
→ 報告者の「データが保存されているのか不安」は的中。上記の操作は保存されていない。
(b) saveState() を直接呼ぶ9箇所 — 保存はされるが直後にクラッシュ
saveState() 内は 718行目 localStorage.setItem(...) が先に実行され、719行目 els.saveState.textContent で例外になる。よって起動時の正規化処理や CNP 関連(2444・2455・2471・2480行目など)は localStorage への保存自体は成功するが、呼び出し元の後続処理(render() / renderCnp() など)が中断される。
3. 最小修正案(1行・1語の修正)
app.js 378行目のセレクタを HTML の実 id に合わせる。app_broken 内のファイルは変更していません。以下は適用すべき差分の提示のみです。
--- a/app.js
+++ b/app.js
@@ -378 +378 @@
- saveState: document.querySelector("#saveStatus"),
+ saveState: document.querySelector("#saveState"),
- 修正はこの1箇所で完結する(
#saveStatusの出現は全ファイルでここだけ)。 - 代替案として index.html 側の id を
saveStatusに変える手もあるが、HTML・CSSクラス・JSプロパティ名の3者が既に「saveState」で揃っているため、規約から外れた app.js 側の1語を直すのが最小かつ整合的。
4. 再発防止の一言
els を組み立てた直後に null チェックを1つ入れると、同種の typo が「操作時のクラッシュ」ではなく「起動時のコンソール警告」で即座に見つかります。
例: Object.entries(els).forEach(([k, v]) => { if (!v) console.warn("要素が見つかりません:", k); });
5. 調査の所要時間
| 項目 | 時刻 |
|---|---|
| 開始 | 2026-07-24 12:15:09 |
| 終了 | 2026-07-24 12:16:42 |
| 実測 | 約1.6分(93秒) |