Receber por email
← Voltar para o catálogo

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.

367estrelas
Atualizado há 4 meses

Ver no GitHub ↗

Como adicionar

/plugin marketplace add aiskillstore/marketplace

O comando exato pode variar conforme o repositório. Confira o README no GitHub.

Para o autor da skill

Cole no README do seu repo

Mostra que sua skill está catalogada na Skillteca, gera backlink e tráfego rastreável.

[![Skillteca](https://www.skillteca.com.br/api/badge/error-handler-advisor/svg)](https://www.skillteca.com.br/skills/error-handler-advisor)

Skills relacionadas

Ver todas de Segurança →

Alerta por categoria

Receba novas skills de Segurança toda segunda

1 email curto, só com as skills novas de Segurança. 4 minutos de leitura, sem spam, cancela com 1 clique.

Você confirma o email no primeiro envio. Sem spam. Cancela com 1 clique.

Ler descrição completa

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(), or panic!()
  • Functions returning Result or Option types
  • 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() or expect() 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)]
Compartilhar X LinkedIn