Skip to content

fix: define an actionable-error policy and surface those errors as toasts (#131) - #134

Merged
so5 merged 5 commits into
mainfrom
fix/actionable-errors-toast
Sep 10, 2026
Merged

so5 merged 5 commits into
mainfrom
fix/actionable-errors-toast

Conversation

@so5

@so5 so5 commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator

Closes #131

背景と目的

プロジェクトの実行・再開が dispatch 前または dispatch 中に失敗した場合、サーバー側のログには記録されるものの、クライアント(ブラウザ)側には何も表示されず、利用者が失敗に気づけない状態となっていました。原因は以下の 2 点です。

  1. ログレベル別の Socket.IO イベント(logERR など)を送出する処理が、ログ画面刷新時に単一の WHEEL_LOG イベントへ統合された際に失われ、クライアントの onLogErr(トースト表示)ハンドラーが発火しなくなっていた。
  2. runProject 系の処理では runDispatcher を待ち合わせず projectOperator が先に ack(true) を返すため、catch 節の ack(err) は Socket.IO の二重 ack となり無視されていた。

加えて、「そのエラーで利用者の作業を止めるべきか」という基準が定義されておらず、logger.error() が「利用者が対処すべきもの」と「内部的・自動リトライ中のもの」の両方に混在して使われていました。

変更方針

エラー通知のポリシーを次のとおり定義し、documentMD/design/error_handling.md の第 4 章に明記しました。

関数 用途 表示
notifyUser(projectRootDir, ...) 利用者が要求した操作が失敗した、または設定を修正しないと先に進めない場合 ログ出力に加えてトースト表示(logERR イベント)
logger.error() / logError() 記録は必要だが利用者の対処が不要な場合(自動リトライ中の転送、内部アサーション、廃止 API の呼び出し、コンポーネント自体に失敗表示される単発のタスク失敗など) ログ出力のみ

実装内容(サーバー側のみ、クライアント変更なし)

  • server/app/logSettings.jsnotifyUser() を追加しました。内部で getLogger(projectRootDir).error(...) を呼び出すため、ログ画面(WHEEL_LOG)およびログファイルへの出力は従来どおりです。これに加えて、対象ルーム(projectRootDir。プロジェクトに紐づかないものは "default")へ logERR イベントを送出します。トーストに表示する文字列では Errormessage のみに縮約し、スタックトレースは含めません(全文はログ画面・ログファイルで確認できます)。
  • server/app 配下のエラーレベルのログ呼び出しをすべて確認し、利用者の対処が必要な箇所(約 30 か所)を notifyUser() へ置き換えました。主な対象は次のとおりです。
    • projectController:実行 / 保存 / 停止処理の失敗、検証・準備・ワークフロー解析フェーズの失敗
    • ファイル・コンポーネント・プロジェクトの各ハンドラー:インポート / エクスポート / アップロード / リネーム / コミット / 新規作成の失敗
    • remoteFileBrowser:リモートホスト未登録、ダウンロード失敗
    • tryToConnect:接続テストの失敗
    • projectOperations:不正なプロジェクト名、Git リポジトリへのアクセス不可
    • executerManager:未定義のジョブスケジューラー指定
    • dispatcher:stage-out 滞留による実行不能(再実行を促すメッセージ。従来の logFatal から変更)
  • ログ出力のみに据え置いた箇所:ファイルブラウザーの読み取り失敗、gfarm・リモートディレクトリ一覧取得エラー、内部アサーション、廃止 API の呼び出し、単発のタスクエラー

上記 2 点目の「ack(err) が無視される」問題は、該当する catch 節が notifyUser() 経由で logERR を直接送出するようになったため、個別の対応は不要となりました。

テスト・Lint

  • notifyUser() の JSDoc を追加しました(AGENTS.md の規約に準拠)。
  • notifyUser() のユニットテストを server/test/app/Logging.js に追加しました(logERR トーストの送出、Errormessage のみに縮約すること、プロジェクトに紐づかない場合の "default" ルームへのフォールバック)。
  • Lint(eslint)を実行し、本変更による新規の指摘が 0 件であることを確認しました(既存の指摘は本 PR の対象外です)。
  • 影響範囲のユニットテストをコンテナ内で実行し、すべて成功することを確認しました(合計 251 件 passing)。
    • test/app/Logging.jstest/app/core/restart.jstest/app/core/dispatcher.jstest/app/core/executerManager.jslogError のスタブを notifyUser のスタブへ追随修正)
    • test/app/core/projectOperations.jstest/app/handlers/projectController.jstest/app/handlers/fileManager.jstest/app/handlers/workflowEditor.js

関連

onLogErr ハンドラー内の正規表現による整形処理は、旧ログ書式を前提としたものであり本変更により不要となりますが、クライアント側の整理は本 PR の対象外とし、別途対応します。

🤖 Generated with Claude Code

https://claude.ai/code/session_01C3jKNM1qubomM8UTRdkEWu

…asts

Errors were logged but never shown to the user: the per-level "logERR"
socket event the client's onLogErr toast handlers listen for stopped
being emitted when the socketIO appender was unified onto "WHEEL_LOG",
and the run path's ack(err) is a dropped second ack (projectOperator
already resolved it with ack(true)). Whether an error should interrupt
the user was also never defined - .error() covered both "you must fix
this" and "internal / auto-retried".

Policy (documentMD/design/error_handling.md #4):
- notifyUser(projectRootDir, ...): the user must act on it (an operation
  they requested failed, or a setting must be fixed) -> logged at error
  level AND emitted on "logERR" so the client shows a snackbar.
- logger.error() / logError(): worth recording, no user action needed
  (auto-retried transfer, internal assertion, deprecated API call, a
  task failure already shown red on its component) -> log only.

notifyUser() lives in logSettings.js. It logs via getLogger().error()
(so WHEEL_LOG / the log file are unchanged) and emits "logERR" to the
project room, or "default" for messages not tied to a project (project
list / remotehost screen / import dialog). Errors are reduced to their
message for the toast; the full entry stays in the log.

Audited every error-level call in server/app and converted the
actionable ones (~30 sites): run/save/stop failures in
projectController, import/export/upload/rename/commit failures in
file/component/project handlers, "host not found" and connection-test
failures, invalid project names, editor-op failures, unknown job
scheduler, and the stage-out-stuck "re-run to continue" message. Left
as log-only: file-browser read failures, gfarm/remote listing errors,
internal asserts, deprecated-API calls, per-task errors.

No client change.

Refs #131

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C3jKNM1qubomM8UTRdkEWu
actions-user and others added 4 commits September 10, 2026 11:09
AGENTS.md requires JSDoc on new functions. No behavior change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C3jKNM1qubomM8UTRdkEWu
Covers: logERR toast emitted alongside the log, Error reduced to its
message (no stack) in the toast, and "default" room fallback for
messages not tied to a project.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C3jKNM1qubomM8UTRdkEWu
@so5
so5 merged commit 25ed44a into main Sep 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

プロジェクト実行/再開時のエラーがクライアントに通知されない(トーストが出ない)

2 participants