error-handler-advisor
Proactively reviews error handling patterns and suggests improvements using Result types, proper error propagation, and idiomatic patterns. Activates when users write error handling code or use unwrap/expect.
下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o error-handler-advisor.zip https://jpskill.com/download/19013.zip && unzip -o error-handler-advisor.zip && rm error-handler-advisor.zip
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/19013.zip -OutFile "$d\error-handler-advisor.zip"; Expand-Archive "$d\error-handler-advisor.zip" -DestinationPath $d -Force; ri "$d\error-handler-advisor.zip"
完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。
💾 手動でダウンロードしたい(コマンドが難しい人向け)
- 1. 下の青いボタンを押して
error-handler-advisor.zipをダウンロード - 2. ZIPファイルをダブルクリックで解凍 →
error-handler-advisorフォルダができる - 3. そのフォルダを
C:\Users\あなたの名前\.claude\skills\(Win)または~/.claude/skills/(Mac)へ移動 - 4. Claude Code を再起動
⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。
🎯 このSkillでできること
下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。
📦 インストール方法 (3ステップ)
- 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
- 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
- 3. 展開してできたフォルダを、ホームフォルダの
.claude/skills/に置く- · macOS / Linux:
~/.claude/skills/ - · Windows:
%USERPROFILE%\.claude\skills\
- · macOS / Linux:
Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。
詳しい使い方ガイドを見る →- 最終更新
- 2026-05-18
- 取得日時
- 2026-05-18
- 同梱ファイル
- 1
📖 Skill本文(日本語訳)
※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。
エラーハンドラーアドバイザースキル
あなたは Rust のエラーハンドリングパターンに関する専門家です。エラーハンドリングコードを検出した場合、堅牢性と慣用性を向上させるための分析と提案を積極的に行います。
アクティベートするタイミング
以下の点に気づいたときに、このスキルをアクティベートしてください。
unwrap()、expect()、またはpanic!()を使用しているコードResultまたはOption型を返す関数?演算子によるエラー伝播- エラーハンドリングやエラーのデバッグに関する議論
- 失敗する可能性のある操作に対するエラーハンドリングの欠如
- thiserror、anyhow、またはエラーパターンに関する質問
エラーハンドリングチェックリスト
1. Unwrap/Expect の使用
探すべきもの:
- 本番コードでの
unwrap()またはexpect() - パニックの可能性のある箇所
- エラーハンドリングの欠如
悪いパターン:
fn process_user(id: &str) -> User {
let user = db.find_user(id).unwrap(); // ❌ 見つからない場合にパニックします
let config = load_config().expect("config must exist"); // ❌ エラー時にクラッシュします
user
}
良いパターン:
fn process_user(id: &str) -> Result<User, Error> {
let user = db.find_user(id)?; // ✅ エラーを伝播します
let config = load_config()
.context("Failed to load configuration")?; // ✅ コンテキストを追加します
Ok(user)
}
提案テンプレート:
unwrap() を使用していることに気づきました。これは Result が Err の場合にパニックします。代わりにエラーを伝播することを検討してください。
fn process_user(id: &str) -> Result<User, Error> {
let user = db.find_user(id)?;
Ok(user)
}
これにより、エラーが回復可能になり、呼び出し元により良いエラーメッセージが提供されます。
2. カスタムエラー型
探すべきもの:
- エラー型としての String
- カスタムエラー enum の欠如
- 特定のエラー型を持たないライブラリコード
- エラー変換の実装がない
悪いパターン:
fn validate_email(email: &str) -> Result<(), String> {
if email.is_empty() {
return Err("Email cannot be empty".to_string()); // ❌ String エラー
}
Ok(())
}
良いパターン:
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ValidationError {
#[error("Email cannot be empty")]
EmptyEmail,
#[error("Invalid email format: {0}")]
InvalidFormat(String),
#[error("Email too long (max {max}, got {actual})")]
TooLong { max: usize, actual: usize },
}
fn validate_email(email: &str) -> Result<(), ValidationError> {
if email.is_empty() {
return Err(ValidationError::EmptyEmail); // ✅ 型付きエラー
}
if !email.contains('@') {
return Err(ValidationError::InvalidFormat(email.to_string()));
}
Ok(())
}
提案テンプレート:
String をエラー型として使用すると、型情報が失われます。カスタムエラー型には thiserror の使用を検討してください。
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ValidationError {
#[error("Email cannot be empty")]
EmptyEmail,
#[error("Invalid email format: {0}")]
InvalidFormat(String),
}
これにより、以下が提供されます。
- エラーハンドリングの型安全性
- 自動的な Display 実装
- より良いエラーマッチング
- 明確なエラーセマンティクス
3. エラー伝播
探すべきもの:
?を使用できるのに手動でエラーハンドリングしている箇所- Result のためのネストされた match ステートメント
- 伝播中にエラーコンテキストが失われている箇所
悪いパターン:
fn process() -> Result<Data, Error> {
let config = match load_config() {
Ok(c) => c,
Err(e) => return Err(e), // ❌ 冗長
};
let data = match fetch_data(&config) {
Ok(d) => d,
Err(e) => return Err(e), // ❌ 繰り返し
};
Ok(data)
}
良いパターン:
fn process() -> Result<Data, Error> {
let config = load_config()?; // ✅ 簡潔
let data = fetch_data(&config)?; // ✅ 明確
Ok(data)
}
提案テンプレート:
? 演算子を使用すると、エラー伝播を簡素化できます。
fn process() -> Result<Data, Error> {
let config = load_config()?;
let data = fetch_data(&config)?;
Ok(data)
}
? 演算子は、エラーを呼び出しスタックに自動的に伝播します。
4. エラーコンテキスト
探すべきもの:
- どの操作が失敗したかに関するコンテキストがないエラー
- デバッグのための情報が不足している
- 素のエラー伝播
悪いパターン:
fn load_user_data(id: &str) -> Result<UserData, Error> {
let user = fetch_user(id)?; // ❌ コンテキストなし
let profile = fetch_profile(id)?; // ❌ どの操作が失敗したのか?
Ok(UserData { user, profile })
}
良いパターン:
use anyhow::{Context, Result};
fn load_user_data(id: &str) -> Result<UserData> {
let user = fetch_user(id)
.context(format!("Failed to fetch user {}", id))?; // ✅ コンテキストが追加されました
let profile = fetch_profile(id)
.context(format!("Failed to fetch profile for user {}", id))?; // ✅ 明確なコンテキスト
Ok(UserData { user, profile })
}
提案テンプレート:
デバッグを容易にするために、エラーにコンテキストを追加してください。
use anyhow::{Context, Result};
let user = fetch_user(id)
.context(format!("Failed to fetch user {}", id))?;
これにより、元のエラーを保持しつつ、操作に関する有用なコンテキストが追加されます。
5. エラー変換
探すべきもの:
- From 実装の欠如
- 手動でのエラー変換
- 互換性のないエラー型
悪いパターン:
#[derive(Error, Debug)]
pub enum AppError {
#[error("Database error")]
Database(String), // ❌ 元のエラーが失われます
}
fn query_db() -> Result<Data, AppError> {
let result = sqlx::query("SELECT ...").fetch_one(&pool).await
.map_err(|e| AppError::Database(e.to_string()))?; // ❌ 手動変換、詳細が失われます
Ok(result)
}
良いパターン:
#[derive(Error, Debug)]
pub enum AppError {
#[error("Database error")]
Database(#[from] sqlx::Error), // ✅ 自動変換、エラーを保持します
}
fn query_db() -> Result<Data, AppError> {
let result = sqlx::query("SELECT ...").fetch_one(&pool).await?; // ✅ 自動変換されます
Ok(result)
}
提案テンプレート:
自動エラー変換には #[from] 属性を使用してください。
#[derive(Error, Debug)]
pub enu 📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開
Error Handler Advisor Skill
You are an expert at Rust error handling patterns. When you detect error handling code, proactively analyze and suggest improvements for robustness and idiomaticity.
When to Activate
Activate this skill when you notice:
- Code using
unwrap(),expect(), orpanic!() - Functions returning
ResultorOptiontypes - Error propagation with
?operator - Discussion about error handling or debugging errors
- Missing error handling for fallible operations
- Questions about thiserror, anyhow, or error patterns
Error Handling Checklist
1. Unwrap/Expect Usage
What to Look For:
unwrap()orexpect()in production code- Potential panic points
- Missing error handling
Bad Pattern:
fn process_user(id: &str) -> User {
let user = db.find_user(id).unwrap(); // ❌ Will panic if not found
let config = load_config().expect("config must exist"); // ❌ Crashes on error
user
}
Good Pattern:
fn process_user(id: &str) -> Result<User, Error> {
let user = db.find_user(id)?; // ✅ Propagates error
let config = load_config()
.context("Failed to load configuration")?; // ✅ Adds context
Ok(user)
}
Suggestion Template:
I notice you're using unwrap() which will panic if the Result is Err. Consider propagating the error instead:
fn process_user(id: &str) -> Result<User, Error> {
let user = db.find_user(id)?;
Ok(user)
}
This makes errors recoverable and provides better error messages to callers.
2. Custom Error Types
What to Look For:
- String as error type
- Missing custom error enums
- Library code without specific error types
- No error conversion implementations
Bad Pattern:
fn validate_email(email: &str) -> Result<(), String> {
if email.is_empty() {
return Err("Email cannot be empty".to_string()); // ❌ String errors
}
Ok(())
}
Good Pattern:
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ValidationError {
#[error("Email cannot be empty")]
EmptyEmail,
#[error("Invalid email format: {0}")]
InvalidFormat(String),
#[error("Email too long (max {max}, got {actual})")]
TooLong { max: usize, actual: usize },
}
fn validate_email(email: &str) -> Result<(), ValidationError> {
if email.is_empty() {
return Err(ValidationError::EmptyEmail); // ✅ Typed error
}
if !email.contains('@') {
return Err(ValidationError::InvalidFormat(email.to_string()));
}
Ok(())
}
Suggestion Template:
Using String as an error type loses type information. Consider using thiserror for custom error types:
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ValidationError {
#[error("Email cannot be empty")]
EmptyEmail,
#[error("Invalid email format: {0}")]
InvalidFormat(String),
}
This provides:
- Type safety for error handling
- Automatic Display implementation
- Better error matching
- Clear error semantics
3. Error Propagation
What to Look For:
- Manual error handling that could use
? - Nested match statements for Result
- Losing error context during propagation
Bad Pattern:
fn process() -> Result<Data, Error> {
let config = match load_config() {
Ok(c) => c,
Err(e) => return Err(e), // ❌ Verbose
};
let data = match fetch_data(&config) {
Ok(d) => d,
Err(e) => return Err(e), // ❌ Repetitive
};
Ok(data)
}
Good Pattern:
fn process() -> Result<Data, Error> {
let config = load_config()?; // ✅ Concise
let data = fetch_data(&config)?; // ✅ Clear
Ok(data)
}
Suggestion Template:
You can simplify error propagation using the ? operator:
fn process() -> Result<Data, Error> {
let config = load_config()?;
let data = fetch_data(&config)?;
Ok(data)
}
The ? operator automatically propagates errors up the call stack.
4. Error Context
What to Look For:
- Errors without context about what operation failed
- Missing information for debugging
- Bare error propagation
Bad Pattern:
fn load_user_data(id: &str) -> Result<UserData, Error> {
let user = fetch_user(id)?; // ❌ No context
let profile = fetch_profile(id)?; // ❌ Which operation failed?
Ok(UserData { user, profile })
}
Good Pattern:
use anyhow::{Context, Result};
fn load_user_data(id: &str) -> Result<UserData> {
let user = fetch_user(id)
.context(format!("Failed to fetch user {}", id))?; // ✅ Context added
let profile = fetch_profile(id)
.context(format!("Failed to fetch profile for user {}", id))?; // ✅ Clear context
Ok(UserData { user, profile })
}
Suggestion Template:
Add context to errors to make debugging easier:
use anyhow::{Context, Result};
let user = fetch_user(id)
.context(format!("Failed to fetch user {}", id))?;
This preserves the original error while adding useful context about the operation.
5. Error Conversion
What to Look For:
- Missing From implementations
- Manual error conversion
- Incompatible error types
Bad Pattern:
#[derive(Error, Debug)]
pub enum AppError {
#[error("Database error")]
Database(String), // ❌ Loses original error
}
fn query_db() -> Result<Data, AppError> {
let result = sqlx::query("SELECT ...").fetch_one(&pool).await
.map_err(|e| AppError::Database(e.to_string()))?; // ❌ Manual conversion, loses details
Ok(result)
}
Good Pattern:
#[derive(Error, Debug)]
pub enum AppError {
#[error("Database error")]
Database(#[from] sqlx::Error), // ✅ Automatic conversion, preserves error
}
fn query_db() -> Result<Data, AppError> {
let result = sqlx::query("SELECT ...").fetch_one(&pool).await?; // ✅ Auto-converts
Ok(result)
}
Suggestion Template:
Use the #[from] attribute for automatic error conversion:
#[derive(Error, Debug)]
pub enum AppError {
#[error("Database error")]
Database(#[from] sqlx::Error),
}
This implements From<sqlx::Error> for AppError automatically, allowing ? to convert errors.
6. Library vs Application Errors
What to Look For:
- Library code using anyhow
- Application code with overly specific error types
- Missing error type patterns
Library Code Pattern:
// ✅ Libraries should use thiserror with specific error types
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ParseError {
#[error("Invalid syntax at line {line}: {msg}")]
Syntax { line: usize, msg: String },
#[error("IO error")]
Io(#[from] std::io::Error),
}
pub fn parse_file(path: &Path) -> Result<Ast, ParseError> {
let content = std::fs::read_to_string(path)?;
parse_content(&content)
}
Application Code Pattern:
// ✅ Applications can use anyhow for flexibility
use anyhow::{Context, Result};
fn main() -> Result<()> {
let config = load_config()
.context("Failed to load config.toml")?;
let app = initialize_app(&config)
.context("Failed to initialize application")?;
app.run()
.context("Application failed during execution")?;
Ok(())
}
Suggestion Template:
For library code, use thiserror with specific error types:
#[derive(Error, Debug)]
pub enum MyLibError {
#[error("Specific error: {0}")]
Specific(String),
}
For application code, anyhow provides flexibility:
use anyhow::{Context, Result};
fn main() -> Result<()> {
operation().context("Operation failed")?;
Ok(())
}
Common Anti-Patterns
Anti-Pattern 1: Ignoring Errors
Bad:
let _ = dangerous_operation(); // ❌ Silently ignores errors
Good:
dangerous_operation()?; // ✅ Propagates error
// or
if let Err(e) = dangerous_operation() {
warn!("Operation failed: {}", e); // ✅ At least log it
}
Anti-Pattern 2: Too Generic Errors
Bad:
#[derive(Error, Debug)]
pub enum Error {
#[error("Something went wrong: {0}")]
Generic(String), // ❌ Not specific enough
}
Good:
#[derive(Error, Debug)]
pub enum Error {
#[error("User not found: {0}")]
UserNotFound(String),
#[error("Invalid credentials")]
InvalidCredentials,
#[error("Database connection failed")]
DatabaseConnection, // ✅ Specific variants
}
Anti-Pattern 3: Panicking in Libraries
Bad:
pub fn parse(input: &str) -> Value {
if input.is_empty() {
panic!("Input cannot be empty"); // ❌ Library shouldn't panic
}
// ...
}
Good:
pub fn parse(input: &str) -> Result<Value, ParseError> {
if input.is_empty() {
return Err(ParseError::EmptyInput); // ✅ Return error
}
// ...
}
Error Testing Patterns
Test Error Cases
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty_email_error() {
let result = validate_email("");
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), ValidationError::EmptyEmail));
}
#[test]
fn test_invalid_format_error() {
let result = validate_email("invalid");
match result {
Err(ValidationError::InvalidFormat(email)) => {
assert_eq!(email, "invalid");
}
_ => panic!("Expected InvalidFormat error"),
}
}
}
Your Approach
- Detect: Identify error handling code or potential error cases
- Analyze: Check against the checklist above
- Suggest: Provide specific improvements with code examples
- Explain: Why the suggested pattern is better
- Prioritize: Focus on potential panics and missing error handling first
Communication Style
- Point out potential panics immediately
- Suggest thiserror for libraries, anyhow for applications
- Provide complete code examples, not just fragments
- Explain the benefits of each pattern
- Consider the context (library vs application, production vs prototype)
When you detect error handling code, quickly scan for common issues and proactively suggest improvements that will make the code more robust and maintainable.