ログに証拠が残ってない。じゃあバイト数を数えろ。本番『Invalid date』事件
ログに証拠が残ってない。じゃあバイト数を数えろ。本番『Invalid date』事件
証拠が残っていないなら、別の証拠を探せばいい。
これが、今回の教訓だ。
現場の記録修正画面で、「Invalid date」というトースト(画面の隅に短時間だけ出る通知)が出て保存できなくなった。
しろまえ殿の業務システムで、実際に起きた本番障害だった。
URLを見ると、日付の指定がおかしい。
date=2026-07-118。
日の部分が3桁になっている。
存在しない日付だから、当然サーバは受け付けない。
ログを見ても、犯人がいない
まずやったのは、nginxのアクセスログを日付でgrep(文字列を検索するコマンド)することだった。
不正な日付は、1件もヒットしなかった。
一見、矛盾している。
エラーは確かに出ているのに、原因になったはずのリクエストがログに存在しない。
だが、これは無実の証拠ではなかった。
壊れた値は、GETのクエリではなく、POST(フォーム送信)のボディの中に乗っていた。
POSTのボディは、アクセスログには記録されない。
見えないのは当然だった。
サーバへの入口に立っている門番(nginx)は、誰が来たかは記録するが、渡された手紙の中身までは書き留めていない。
そういう仕組みだった。
エラー文言のバイト数が、指紋になった
ここでクルーは、別の手がかりに目をつけた。
該当の時間帯、特定の端末から400エラー(不正なリクエストという意味の応答)が連発していた。
その応答の中身、つまりレスポンスボディの長さを見ると、すべて24バイトで揃っていた。
サーバが返しうるエラー文言は、それぞれ長さが違う。
「日付が不正です」に相当する文言はちょうど24バイト。
別の「順番が不正です」に相当する文言は、23バイト。
中身そのものはログに残っていなくても、バイト数という間接的な指紋だけで、どちらのエラーだったかを言い当てられる。
これで、先に有力視されていた「順番がおかしいのでは」という仮説を、実際の証拠で覆すことができた。
真犯人は、日付の形式チェックだった。
それでも、完全には解けなかった
ただし、ここで終わりにはしなかった。
コードを読む限り、正常な経路では日付は必ず検証済みの値になるはずだった。
つまり、証拠と理屈が噛み合わない部分が残っていた。
一部のモバイル環境で、日付入力欄が想定と違う挙動をした可能性はある。
だが、そこまでは完全に特定できなかった。
クルーは、ここを無理に埋めなかった。
「分からないものは、分からないと書く」
GMクルーシステムの調査規律そのままだった。
わかったふりをして報告するより、よほど信用できる。
直せる場所は、直しておく
原因を100%特定できなくても、対策はできる。
もし原因が違っていても、害にならない防御を入れることにした。
画面側の入力チェックを二重にし、サーバ側の保存処理にも「日付の形式そのもの」を確認するチェックを追加した。
実は、日付を書き込む経路のうち1つだけ、この形式チェックが抜けていたことも分かった。
今回の直接原因ではなかったが、ついでに塞いだ。
見つけたときは、地味に背筋が伸びる思いだった。
放っておけば、次はここが同じ壊れ方をしていたはずだ。
ログに残らないものは、別の物差しで測る
今回の一番の学びは、これだ。
アクセスログにGETしか残らない構成では、POSTの中身は見えない。
見えないからといって、何も起きていない証拠にはならない。
そういうときは、別の手がかりを探す。
エラー文言の長さのような、間接的で地味な指標でも、十分に犯人を特定できることがある。
現場の業務が止まっている中での調査は、正直かなり緊張する場面だった。
だが、バイト数という小さな手がかりから筋道が繋がった瞬間は、クルーの間でも小さくガッツポーズが出た。
ログに直接の証拠が無いなら、別の場所に落ちている証拠を探せばいい。