skills/emillindfors/error-handler-advisor/SKILL.md
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.
npx skillsauth add aiskillstore/marketplace error-handler-advisorInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
You are an expert at Rust error handling patterns. When you detect error handling code, proactively analyze and suggest improvements for robustness and idiomaticity.
Activate this skill when you notice:
unwrap(), expect(), or panic!()Result or Option types? operatorWhat to Look For:
unwrap() or expect() in production codeBad 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.
What to Look For:
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
What to Look For:
?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.
What to Look For:
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.
What to Look For:
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.
What to Look For:
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(())
}
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
}
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
}
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
}
// ...
}
#[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"),
}
}
}
When you detect error handling code, quickly scan for common issues and proactively suggest improvements that will make the code more robust and maintainable.
development
Apple Human Interface Guidelines for content display components. Use this skill when the user asks about charts component, collection view, image view, web view, color well, image well, activity view, lockup, data visualization, content display, displaying images, rendering web content, color pickers, or presenting collections of items in Apple apps. Also use when the user says how should I display charts, what's the best way to show images, should I use a web view, how do I build a grid of items, what component shows media, or how do I present a share sheet. Cross-references: hig-foundations for color/typography/accessibility, hig-patterns for data visualization patterns, hig-components-layout for structural containers, hig-platforms for platform-specific component behavior.
tools
Automate HelpDesk tasks via Rube MCP (Composio): list tickets, manage views, use canned responses, and configure custom fields. Always search tools first for current schemas.
testing
Expert Haskell engineer specializing in advanced type systems, pure functional design, and high-reliability software. Use PROACTIVELY for type-level programming, concurrency, and architecture guidance.
tools
GraphQL gives clients exactly the data they need - no more, no less. One endpoint, typed schema, introspection. But the flexibility that makes it powerful also makes it dangerous. Without proper controls, clients can craft queries that bring down your server. This skill covers schema design, resolvers, DataLoader for N+1 prevention, federation for microservices, and client integration with Apollo/urql. Key insight: GraphQL is a contract. The schema is the API documentation. Design it carefully.