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.
What this skill does
# 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**:
```rust
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**:
```rust
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**:
```rust
fn validate_email(email: &str) -> Result<(), String> {
if email.is_empty() {
return Err("Email cannot be empty".to_string()); // ❌ String errors
}
Ok(())
}
```
**Good Pattern**:
```rust
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**:
```rust
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**:
```rust
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**:
```rust
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**:
```rust
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**:
```rust
#[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**:
```rust
#[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**:
```rust
// ✅ 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**:
```rust
// ✅ 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**:
```rust
let _ = dangerous_operation(); // ❌ Silently ignores errors
```
**Good**:
```rust
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**:
```rust
#[derive(Error, Debug)]Related in General
modeling-omnistudio-epc-catalog
IncludedSalesforce Industries CME EPC product-modeling skill for Product2-based catalog creation. Use when creating EPC products, configuring product attributes, building offer bundles with Product Child Items, or reviewing EPC DataPack JSON metadata for product catalog changes. TRIGGER when: user creates or updates Product2 EPC records, AttributeAssignment payloads, AttributeMetadata/AttributeDefaultValues, Offer bundles, or ProductChildItem relationships. DO NOT TRIGGER when: designing OmniScripts/FlexCards/Integration Procedures (use building-omnistudio-omniscript, building-omnistudio-flexcard, or building-omnistudio-integration-procedure), implementing Apex business logic (use generating-apex), or troubleshooting deployment pipelines (use deploying-metadata).
relationship-science-coach
IncludedUse this skill for direct, practical adult relationship coaching: couples conflict, repair, trust, marriage, dating, flirting, attachment patterns, emotional connection, sex, desire differences, eroticism, kink negotiation, affection, love languages, breakups, and long-term passion. Draw on Gottman, EFT and Hold Me Tight, attachment science, modern sex research, Perel, Nagoski, Kerner, Schnarch, Love and Stosny, and flexible love-language tools. Be concrete and low-hedge. Redirect only for imminent danger, abuse, coercive control, minors, non-consent, self-harm, stalking, or medical/legal/psychiatric decisions.
building-sf-integrations
IncludedSalesforce integration architecture and runtime plumbing with 120-point scoring. Use this skill to set up Named Credentials, External Credentials, External Services, REST/SOAP callout patterns, Platform Events, and Change Data Capture. TRIGGER when: user sets up Named Credentials, External Services, REST/SOAP callouts, Platform Events, CDC, or touches .namedCredential-meta.xml files. DO NOT TRIGGER when: Connected App/OAuth config (use configuring-connected-apps), Apex-only logic (use generating-apex), or data import/export (use handling-sf-data).
venue-templates
IncludedAccess comprehensive LaTeX templates, formatting requirements, and submission guidelines for major scientific publication venues (Nature, Science, PLOS, IEEE, ACM), academic conferences (NeurIPS, ICML, CVPR, CHI), research posters, and grant proposals (NSF, NIH, DOE, DARPA). This skill should be used when preparing manuscripts for journal submission, conference papers, research posters, or grant proposals and need venue-specific formatting requirements and templates.
let-fate-decide
IncludedDraws the 12 Houses of the Zodiac Tarot spread to inject entropy into planning when prompts are vague, ambiguous, or casually delegated. Interprets the spread to guide next steps. Use when the user says 'let fate decide', 'YOLO', 'whatever', 'idk', or other nonchalant phrases, makes Yu-Gi-Oh references, or when you are about to arbitrarily pick between multiple reasonable approaches. Prefer over ask-questions-if-underspecified when the user's tone is casual or playful rather than precision-seeking.
net-ops
IncludedCross-platform network troubleshooting (Windows, macOS, Linux) via local or remote shell. Use for: DNS broken, can't resolve hostnames, nslookup/dig works but apps fail, NRPT, WFP, scutil, /etc/resolver, systemd-resolved, /etc/resolv.conf, NetworkManager, VPN DNS leak residue (ProtonVPN/Mullvad/WireGuard/AnyConnect), AV/firewall blocking DNS or DoH, Tailscale DNS interaction, intermittent connectivity, remote diagnostics over SSH.