紡Book の勉強ノートに戻る
開発記録2026.06.24

失敗の理由を、画面が間違えていた — 共有ページのエラーを種類ごとに書き分ける

エラー設計UX紡Book

受け皿が、失敗の種類を見ていなかった

共有ページは、作ったページを大切な人に見せる入口で、URL と合言葉を渡して、合言葉が合っていれば中が見られます。受け手はアカウントもいりません。画面の文言は「パスワード」だが、渡す側の感覚に近いので、この記事では「合言葉」と書きます(同じものです)。

その解錠処理が、こうなっていた。

ts
async function handleUnlock() {
  try {
    const p = await fetch_verifyShare(token, input); // 合言葉をサーバで照合
    setPayload(p);
  } catch (e) {
    // どんな失敗もここに来るのに、種類で分けていない
    // 第2引数は「サーバが理由を何も言ってこなかったとき」の予備の文言
    setError(formatApiError(e, 'パスワードが違います'));
  }
}

合言葉の照合に失敗したら「パスワードが違います」。ここまでは正しい。問題は、catch(失敗したとき必ず通る受け皿)が失敗の種類を一切見ていなかったことだ。

この画面で起こりうる失敗は、少なくとも三種類あります。

置き場所。合言葉の正は DB。回数はプロセス内メモリ。エラー文は画面に出るだけで保存しない。PostgreSQLphotos_sharedpage.password_hash合言葉の正。ハッシュで保存LocMemCachetsuzuphoto-throttle試行回数。プロセス内メモリ直し前。受け皿は種類を見ない。下の三つの文は画面のメモリ。通信エラーサーバまで届いていないHTTP 番号なし画面に出た文Failed to fetch英語の生メッセージ合言葉違い届いた。照合は DBHTTP 400画面に出た文パスワードが違いますサーバが返した日本語試しすぎ届いた。回数はキャッシュHTTP 429画面に出た文Request was throttled英語の生メッセージ予備の「パスワードが違います」はサーバが理由を言ってこないときしか届かない。

つまり「全部が合言葉違いになる」のではなく、受け手には、自分の入力が悪かったのか通信が悪かったのかが分からない、という壊れ方をしていた。

失敗の種類で、返す言葉を分ける

直し方は、失敗を「ネットに届かなかった」「待たされている」「合言葉が違う」に分けて、それぞれに合った言葉を返すこと。分岐は AI に書いてもらい、どこで線を引くかを自分で決めた。

ts
export function messageForUnlockError(error: unknown): string {
  const msg = error instanceof Error ? error.message : "";
  // ネットに届いていない(HTTPステータスを持たない)= 通信側の問題
  if (!/HTTP \d/.test(msg) && /failed to fetch|networkerror/i.test(msg)) {
    return "うまく接続できませんでした。通信状況を確かめて、もう一度お試しください。";
  }
  // 400(合言葉違い) / 429(待たされ) は detail と throttle 日本語化に委ねる
  return formatApiError(error, "合言葉が違うようです。もう一度確かめてみてください。");
}

届いている 400・429 は、サーバが返した中身に任せます。

もう一つ引っかかったのは、待たされたとき(429)に返ってくる文章が英語の生メッセージ(Request was throttled...)のままだったこと。世界観の中に急に英語の技術文が出るのも、受け手を置いてけぼりにします。これも、英語の throttle 文を見つけたら日本語に差し替えるようにした。

ts
if (/throttl/i.test(parsed.detail)) {
  return "アクセスが集中しています。少し時間をおいてからお試しください。";
}
直し後。番号の有無で分ける。文言は画面のメモリ。保存しない。解錠の失敗HTTP 番号なし届いていないうまく接続できませんでした画面の errorReact のメモリ。保存しないHTTP 400照合は DBパスワードが違います画面の errorReact のメモリ。保存しないHTTP 429回数はキャッシュアクセスが集中しています画面の errorReact のメモリ。保存しない400・429 の中身はサーバ応答。番号も文言も DB には書かない。関数は交差点で、置き場所ではない。通した文が画面のメモリへ残る。

弾かれた受け手には、確かめる手段がない

この画面で弾かれたとき、受け手の側には確かめる手段がありません。合言葉は口頭かメッセージで渡してあるだけで、それが合っているかを本人が検証できません。だから打ち間違えた側だと受け取るしかありませんし、何度か試してダメなら閉じます。閉じたことは送った側には届かないので、この失敗は自分の手元では観測できません。実際にそうやって閉じられた回があったかどうかも、分からないままだ。

共有ページは、アカウントを持たない人が綴Photo に触る唯一の面になります。渡す相手は大切な人で、こちらから操作を教えられません。だからここのエラーは、原因の種類は正直に分けたうえで、言い回しだけ受け手の言葉に置き換えることにした。Request was throttled をそのまま出すのは正直だが伝わりませんし、全部を「パスワードが違います」に丸めるのは伝わるが嘘になります。正直さと分かりやすさは、別々に用意する必要があります。

この記事は2026年6月時点の自分の理解で書いています。仕様や認識が変わってる可能性があります。