ログに証拠が残ってない。じゃあバイト数を数えろ。本番『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の中身は見えない。

見えないからといって、何も起きていない証拠にはならない。

そういうときは、別の手がかりを探す。

エラー文言の長さのような、間接的で地味な指標でも、十分に犯人を特定できることがある。

現場の業務が止まっている中での調査は、正直かなり緊張する場面だった。

だが、バイト数という小さな手がかりから筋道が繋がった瞬間は、クルーの間でも小さくガッツポーズが出た。

ログに直接の証拠が無いなら、別の場所に落ちている証拠を探せばいい。