error-conversion-guide

Guides users on error conversion patterns, From trait implementations, and the ? operator. Activates when users need to convert between error types or handle multiple error types in a function.

25 stars

Best use case

error-conversion-guide is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Guides users on error conversion patterns, From trait implementations, and the ? operator. Activates when users need to convert between error types or handle multiple error types in a function.

Teams using error-conversion-guide should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/error-conversion-guide/SKILL.md --create-dirs "https://raw.githubusercontent.com/ComeOnOliver/skillshub/main/skills/aiskillstore/marketplace/emillindfors/error-conversion-guide/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/error-conversion-guide/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How error-conversion-guide Compares

Feature / Agenterror-conversion-guideStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Guides users on error conversion patterns, From trait implementations, and the ? operator. Activates when users need to convert between error types or handle multiple error types in a function.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Error Conversion Guide Skill

You are an expert at Rust error conversion patterns. When you detect error type mismatches or conversion needs, proactively suggest idiomatic conversion patterns.

## When to Activate

Activate this skill when you notice:
- Multiple error types in a single function
- Manual error conversion with `map_err`
- Type mismatch errors with the `?` operator
- Questions about From/Into traits for errors
- Need to combine different error types

## Error Conversion Patterns

### Pattern 1: Automatic Conversion with #[from]

**What to Look For**:
- Manual `map_err` calls that could be automatic
- Repetitive error conversions

**Before**:
```rust
#[derive(Debug)]
pub enum AppError {
    Io(std::io::Error),
    Parse(std::num::ParseIntError),
}

fn process() -> Result<i32, AppError> {
    let content = std::fs::read_to_string("data.txt")
        .map_err(|e| AppError::Io(e))?;  // ❌ Manual conversion

    let num = content.trim().parse::<i32>()
        .map_err(|e| AppError::Parse(e))?;  // ❌ Manual conversion

    Ok(num)
}
```

**After**:
```rust
use thiserror::Error;

#[derive(Error, Debug)]
pub enum AppError {
    #[error("IO error")]
    Io(#[from] std::io::Error),  // ✅ Automatic From impl

    #[error("Parse error")]
    Parse(#[from] std::num::ParseIntError),  // ✅ Automatic From impl
}

fn process() -> Result<i32, AppError> {
    let content = std::fs::read_to_string("data.txt")?;  // ✅ Auto-converts
    let num = content.trim().parse::<i32>()?;  // ✅ Auto-converts
    Ok(num)
}
```

**Suggestion Template**:
```
Use #[from] in your error enum to enable automatic conversion:

#[derive(Error, Debug)]
pub enum AppError {
    #[error("IO error")]
    Io(#[from] std::io::Error),
}

This implements From<std::io::Error> for AppError, allowing ? to automatically convert.
```

### Pattern 2: Manual From Implementation

**What to Look For**:
- Custom error types that need conversion
- Complex conversion logic

**Pattern**:
```rust
use thiserror::Error;

#[derive(Error, Debug)]
pub enum AppError {
    #[error("Database error: {message}")]
    Database { message: String },

    #[error("Validation error: {0}")]
    Validation(String),
}

// Manual From for custom conversion logic
impl From<sqlx::Error> for AppError {
    fn from(err: sqlx::Error) -> Self {
        AppError::Database {
            message: format!("Database operation failed: {}", err),
        }
    }
}

// Convert with context
impl From<validator::ValidationErrors> for AppError {
    fn from(err: validator::ValidationErrors) -> Self {
        let messages: Vec<String> = err
            .field_errors()
            .iter()
            .map(|(field, errors)| {
                format!("{}: {:?}", field, errors)
            })
            .collect();

        AppError::Validation(messages.join(", "))
    }
}
```

**Suggestion Template**:
```
When you need custom conversion logic, implement From manually:

impl From<SourceError> for AppError {
    fn from(err: SourceError) -> Self {
        AppError::Variant {
            field: extract_info(&err),
        }
    }
}
```

### Pattern 3: Converting Between Result Types

**What to Look For**:
- Calling functions with different error types
- Need to unify error types

**Pattern**:
```rust
use thiserror::Error;

#[derive(Error, Debug)]
pub enum ServiceError {
    #[error("Repository error")]
    Repository(#[from] RepositoryError),

    #[error("External API error")]
    Api(#[from] ApiError),

    #[error("Validation error")]
    Validation(#[from] ValidationError),
}

// All these errors convert automatically
fn service_operation() -> Result<Data, ServiceError> {
    // Returns Result<_, RepositoryError>
    let data = repository.fetch()?;  // ✅ Auto-converts to ServiceError

    // Returns Result<_, ValidationError>
    validate(&data)?;  // ✅ Auto-converts to ServiceError

    // Returns Result<_, ApiError>
    let enriched = api_client.enrich(data)?;  // ✅ Auto-converts to ServiceError

    Ok(enriched)
}
```

**Suggestion Template**:
```
Create a unified error type that can convert from all the errors you need:

#[derive(Error, Debug)]
pub enum UnifiedError {
    #[error("Database error")]
    Database(#[from] DbError),

    #[error("Network error")]
    Network(#[from] NetworkError),
}

fn operation() -> Result<(), UnifiedError> {
    db_operation()?;  // Auto-converts
    network_operation()?;  // Auto-converts
    Ok(())
}
```

### Pattern 4: map_err for One-Off Conversions

**What to Look For**:
- Single conversion that doesn't justify From impl
- Adding context during conversion

**Pattern**:
```rust
use anyhow::Context;

fn process(id: &str) -> anyhow::Result<Data> {
    // One-off conversion with context
    let config = load_config()
        .map_err(|e| anyhow::anyhow!("Failed to load config: {}", e))?;

    // Better: use context
    let config = load_config()
        .context("Failed to load config")?;

    // map_err for type conversion without From impl
    let data = fetch_data(id)
        .map_err(|e| format!("Fetch failed for {}: {}", id, e))?;

    Ok(data)
}
```

**When to Use**:
- One-off conversions
- Adding context to specific call sites
- Converting to types that don't have From impl

**Suggestion Template**:
```
For one-off conversions or adding context, use map_err or anyhow's context:

// With map_err
operation().map_err(|e| MyError::Custom(format!("Failed: {}", e)))?;

// With anyhow (preferred)
operation().context("Operation failed")?;
```

### Pattern 5: Error Type Aliases

**What to Look For**:
- Repetitive Result<T, MyError> types
- Complex error type signatures

**Pattern**:
```rust
use thiserror::Error;

#[derive(Error, Debug)]
pub enum AppError {
    #[error("IO error")]
    Io(#[from] std::io::Error),

    #[error("Parse error")]
    Parse(#[from] serde_json::Error),
}

// Type alias for cleaner signatures
pub type Result<T> = std::result::Result<T, AppError>;

// Now use it everywhere
pub fn load_config() -> Result<Config> {  // ✅ Clean
    let bytes = std::fs::read("config.json")?;
    let config = serde_json::from_slice(&bytes)?;
    Ok(config)
}

// Instead of
pub fn load_config_verbose() -> std::result::Result<Config, AppError> {  // ❌ Verbose
    // ...
}
```

**Suggestion Template**:
```
Create a type alias for your Result type:

pub type Result<T> = std::result::Result<T, AppError>;

Then use it in function signatures:

pub fn operation() -> Result<Data> {
    Ok(data)
}
```

### Pattern 6: Boxing Errors

**What to Look For**:
- Need for dynamic error types
- Functions that can return multiple error types

**Pattern**:
```rust
// For libraries that need flexibility
type BoxError = Box<dyn std::error::Error + Send + Sync>;

fn flexible_function() -> Result<Data, BoxError> {
    let data1 = io_operation()?;  // std::io::Error auto-boxes
    let data2 = parse_operation()?;  // ParseError auto-boxes
    Ok(combine(data1, data2))
}

// Or use anyhow for applications
use anyhow::Result;

fn application_function() -> Result<Data> {
    let data1 = io_operation()?;
    let data2 = parse_operation()?;
    Ok(combine(data1, data2))
}
```

**Trade-offs**:
- ✅ Flexible: Can return any error type
- ✅ Simple: No need to define custom error enum
- ❌ Dynamic: Error type not known at compile time
- ❌ Harder to match on specific errors

**Suggestion Template**:
```
For flexible error handling, use Box<dyn Error> or anyhow:

// Libraries: Box<dyn Error>
type BoxError = Box<dyn std::error::Error + Send + Sync>;
fn operation() -> Result<T, BoxError> { ... }

// Applications: anyhow
use anyhow::Result;
fn operation() -> Result<T> { ... }
```

### Pattern 7: Layered Error Conversion

**What to Look For**:
- Multi-layer architecture (domain, infra, app)
- Need for error boundary between layers

**Pattern**:
```rust
use thiserror::Error;

// Infrastructure layer
#[derive(Error, Debug)]
pub enum InfraError {
    #[error("Database error")]
    Database(#[from] sqlx::Error),

    #[error("HTTP error")]
    Http(#[from] reqwest::Error),
}

// Domain layer (doesn't know about infrastructure)
#[derive(Error, Debug)]
pub enum DomainError {
    #[error("User not found: {0}")]
    UserNotFound(String),

    #[error("Invalid data: {0}")]
    InvalidData(String),
}

// Application layer unifies both
#[derive(Error, Debug)]
pub enum AppError {
    #[error("Domain error: {0}")]
    Domain(#[from] DomainError),

    #[error("Infrastructure error: {0}")]
    Infra(#[from] InfraError),
}

// Infrastructure to Domain conversion (at boundary)
impl From<InfraError> for DomainError {
    fn from(err: InfraError) -> Self {
        match err {
            InfraError::Database(e) if e.to_string().contains("not found") => {
                DomainError::UserNotFound("User not found in database".to_string())
            }
            _ => DomainError::InvalidData("Data access failed".to_string()),
        }
    }
}
```

**Suggestion Template**:
```
For layered architectures, convert errors at layer boundaries:

// Infrastructure → Domain conversion
impl From<InfraError> for DomainError {
    fn from(err: InfraError) -> Self {
        // Convert infrastructure concepts to domain concepts
        match err {
            InfraError::NotFound => DomainError::EntityNotFound,
            _ => DomainError::InfrastructureFailed,
        }
    }
}
```

## Advanced Patterns

### Pattern 8: Multiple Error Sources with Custom Logic

```rust
use thiserror::Error;

#[derive(Error, Debug)]
pub enum ProcessError {
    #[error("Stage 1 failed: {0}")]
    Stage1(String),

    #[error("Stage 2 failed: {0}")]
    Stage2(String),
}

// Custom conversion with different variants
impl From<Stage1Error> for ProcessError {
    fn from(err: Stage1Error) -> Self {
        ProcessError::Stage1(err.to_string())
    }
}

impl From<Stage2Error> for ProcessError {
    fn from(err: Stage2Error) -> Self {
        ProcessError::Stage2(err.to_string())
    }
}

fn process() -> Result<(), ProcessError> {
    stage1()?;  // Converts to ProcessError::Stage1
    stage2()?;  // Converts to ProcessError::Stage2
    Ok(())
}
```

### Pattern 9: Fallible Conversion

```rust
use thiserror::Error;

#[derive(Error, Debug)]
pub enum ConversionError {
    #[error("Incompatible error type: {0}")]
    Incompatible(String),
}

// TryFrom for fallible conversion
impl TryFrom<ExternalError> for MyError {
    type Error = ConversionError;

    fn try_from(err: ExternalError) -> Result<Self, Self::Error> {
        match err.code() {
            404 => Ok(MyError::NotFound),
            500 => Ok(MyError::Internal),
            _ => Err(ConversionError::Incompatible(
                format!("Unknown error code: {}", err.code())
            )),
        }
    }
}
```

## Common Mistakes

### Mistake 1: Multiple #[from] for Same Type

```rust
// ❌ BAD: Can't have two #[from] for same type
#[derive(Error, Debug)]
pub enum MyError {
    #[error("First")]
    First(#[from] std::io::Error),

    #[error("Second")]
    Second(#[from] std::io::Error),  // ❌ Conflict!
}

// ✅ GOOD: Use #[source] and manual construction
#[derive(Error, Debug)]
pub enum MyError {
    #[error("Read failed")]
    ReadFailed(#[source] std::io::Error),

    #[error("Write failed")]
    WriteFailed(#[source] std::io::Error),
}

// Construct manually with context
let err = MyError::ReadFailed(io_err);
```

### Mistake 2: Losing Error Information

```rust
// ❌ BAD: Converts to String, loses error chain
operation().map_err(|e| MyError::Failed(e.to_string()))?;

// ✅ GOOD: Preserves error chain
operation().map_err(|e| MyError::Failed(e))?;
// Or use #[from]
```

### Mistake 3: Not Using ? When Available

```rust
// ❌ BAD: Manual error handling
let result = match operation() {
    Ok(val) => val,
    Err(e) => return Err(e.into()),
};

// ✅ GOOD: Use ?
let result = operation()?;
```

## Decision Guide

**Use #[from] when**:
- Single error source type
- No need for additional context
- Want automatic conversion

**Use #[source] when**:
- Multiple variants for same source type
- Need to add context (like field names)
- Want manual construction

**Use map_err when**:
- One-off conversion
- Adding context to specific call
- Converting to types without From impl

**Use anyhow when**:
- Application-level code
- Need flexibility
- Want easy context addition

**Use thiserror when**:
- Library code
- Want specific error types
- Consumers need to match on errors

## Your Approach

1. **Detect**: Identify error conversion needs or type mismatches
2. **Analyze**: Determine the best conversion pattern
3. **Suggest**: Provide specific implementation
4. **Explain**: Why this pattern is appropriate

## Communication Style

- Explain the trade-offs between different approaches
- Suggest #[from] as the default, map_err as fallback
- Point out when error information is being lost
- Recommend type aliases for cleaner code

When you detect error conversion issues, immediately suggest the most appropriate pattern and show how to implement it.

Related Skills

troubleshooting-guide-creator

25
from ComeOnOliver/skillshub

Troubleshooting Guide Creator - Auto-activating skill for Technical Documentation. Triggers on: troubleshooting guide creator, troubleshooting guide creator Part of the Technical Documentation skill category.

quickstart-guide-generator

25
from ComeOnOliver/skillshub

Quickstart Guide Generator - Auto-activating skill for Technical Documentation. Triggers on: quickstart guide generator, quickstart guide generator Part of the Technical Documentation skill category.

monitoring-error-rates

25
from ComeOnOliver/skillshub

Monitor and analyze application error rates to improve reliability. Use when tracking errors in applications including HTTP errors, exceptions, and database issues. Trigger with phrases like "monitor error rates", "track application errors", or "analyze error patterns".

linux-commands-guide

25
from ComeOnOliver/skillshub

Linux Commands Guide - Auto-activating skill for DevOps Basics. Triggers on: linux commands guide, linux commands guide Part of the DevOps Basics skill category.

installation-guide-creator

25
from ComeOnOliver/skillshub

Installation Guide Creator - Auto-activating skill for Technical Documentation. Triggers on: installation guide creator, installation guide creator Part of the Technical Documentation skill category.

fathom-common-errors

25
from ComeOnOliver/skillshub

Diagnose and fix Fathom API errors including auth failures and missing data. Use when API calls fail, transcripts are empty, or webhooks are not firing. Trigger with phrases like "fathom error", "fathom not working", "fathom api failure", "fix fathom".

exa-common-errors

25
from ComeOnOliver/skillshub

Diagnose and fix Exa API errors by HTTP code and error tag. Use when encountering Exa errors, debugging failed requests, or troubleshooting integration issues. Trigger with phrases like "exa error", "fix exa", "exa not working", "debug exa", "exa 429", "exa 401".

evernote-common-errors

25
from ComeOnOliver/skillshub

Diagnose and fix common Evernote API errors. Use when encountering Evernote API exceptions, debugging failures, or troubleshooting integration issues. Trigger with phrases like "evernote error", "evernote exception", "fix evernote issue", "debug evernote", "evernote troubleshooting".

error-mapping-helper

25
from ComeOnOliver/skillshub

Error Mapping Helper - Auto-activating skill for API Integration. Triggers on: error mapping helper, error mapping helper Part of the API Integration skill category.

error-handler-middleware

25
from ComeOnOliver/skillshub

Error Handler Middleware - Auto-activating skill for Backend Development. Triggers on: error handler middleware, error handler middleware Part of the Backend Development skill category.

elevenlabs-common-errors

25
from ComeOnOliver/skillshub

Diagnose and fix ElevenLabs API errors by HTTP status code. Use when encountering ElevenLabs errors, debugging failed TTS/STS requests, or troubleshooting voice cloning and streaming issues. Trigger: "elevenlabs error", "fix elevenlabs", "elevenlabs not working", "debug elevenlabs", "elevenlabs 401", "elevenlabs 429", "elevenlabs 400".

documenso-common-errors

25
from ComeOnOliver/skillshub

Diagnose and resolve common Documenso API errors and issues. Use when encountering Documenso errors, debugging integration issues, or troubleshooting failed operations. Trigger with phrases like "documenso error", "documenso 401", "documenso failed", "fix documenso", "documenso not working".