| name | error-handling-completeness |
| description | Evaluates if error handling is sufficient for new code - checks try-catch coverage, logging, user messages, retry logic. Focuses on external calls and user-facing code. |
Error Handling Completeness Skill
Purpose: Prevent production crashes with systematic error handling.
Trigger Words: API call, external, integration, network, database, file, user input, async, promise, await
Quick Decision: Needs Error Handling Check?
def needs_error_check(code_context: dict) -> bool:
"""Decide if error handling review is needed."""
# High-risk operations (always check)
high_risk = [
"fetch", "axios", "requests", "http", # HTTP calls
"db.", "query", "execute", # Database
"open(", "read", "write", # File I/O
"json.loads", "json.parse", # JSON parsing
"int(", "float(", # Type conversions
"subprocess", "exec", # External processes
"await", "async", # Async operations
]
code = code_context.get("code", "").lower()
return any(risk in code for risk in high_risk)
Error Handling Checklist (Fast)
1. External API Calls (Most Critical)
# ❌ BAD - No error handling
def get_user_data(user_id):
response = requests.get(f"https://api.example.com/users/{user_id}")
return response.json() # What if network fails? 404? Timeout?
# ✅ GOOD - Complete error handling
def get_user_data(user_id):
try:
response = requests.get(
f"https://api.example.com/users/{user_id}",
timeout=5 # Timeout!
)
response.raise_for_status() # Check HTTP errors
return response.json()
except requests.Timeout:
logger.error(f"Timeout fetching user {user_id}")
raise ServiceUnavailableError("User service timeout")
except requests.HTTPError as e:
if e.response.status_code == 404:
raise UserNotFoundError(f"User {user_id} not found")
logger.error(f"HTTP error fetching user: {e}")
raise
except requests.RequestException as e:
logger.error(f"Network error: {e}")
raise ServiceUnavailableError("Cannot reach user service")
Quick Checks:
- ✅ Timeout set?
- ✅ HTTP errors handled?
- ✅ Network errors caught?
- ✅ Logged?
- ✅ User-friendly error returned?
2. Database Operations
# ❌ BAD - Swallows errors
def delete_user(user_id):
try:
db.execute("DELETE FROM users WHERE id = ?", [user_id])
except Exception:
pass # Silent failure!
# ✅ GOOD - Specific handling
def delete_user(user_id):
try:
result = db.execute("DELETE FROM users WHERE id = ?", [user_id])
if result.rowcount == 0:
raise UserNotFoundError(f"User {user_id} not found")
except db.IntegrityError as e:
logger.error(f"Cannot delete user {user_id}: {e}")
raise DependencyError("User has related records")
except db.OperationalError as e:
logger.error(f"Database error: {e}")
raise DatabaseUnavailableError()
Quick Checks:
- ✅ Specific exceptions (not bare
except)? - ✅ Logged?
- ✅ User-friendly error?
3. File Operations
# ❌ BAD - File might not exist
def read_config():
with open("config.json") as f:
return json.load(f)
# ✅ GOOD - Handle missing file
def read_config():
try:
with open("config.json") as f:
return json.load(f)
except FileNotFoundError:
logger.warning("config.json not found, using defaults")
return DEFAULT_CONFIG
except json.JSONDecodeError as e:
logger.error(f"Invalid JSON in config.json: {e}")
raise ConfigurationError("Malformed config.json")
except PermissionError:
logger.error("Permission denied reading config.json")
raise
Quick Checks:
- ✅ FileNotFoundError handled?
- ✅ JSON parse errors caught?
- ✅ Permission errors handled?
4. Type Conversions
# ❌ BAD - Crash on invalid input
def process_age(age_str):
age = int(age_str) # What if "abc"?
return age * 2
# ✅ GOOD - Validated
def process_age(age_str):
try:
age = int(age_str)
if age < 0 or age > 150:
raise ValueError("Age out of range")
return age * 2
except ValueError:
raise ValidationError(f"Invalid age: {age_str}")
Quick Checks:
- ✅ ValueError caught?
- ✅ Range validation?
- ✅ Clear error message?
5. Async/Await (JavaScript/Python)
// ❌ BAD - Unhandled promise rejection
async function fetchUser(id) {
const user = await fetch(`/api/users/${id}`);
return user.json(); // What if network fails?
}
// ✅ GOOD - Handled
async function fetchUser(id) {
try {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return await response.json();
} catch (error) {
console.error(`Failed to fetch user ${id}:`, error);
throw new ServiceError("Cannot fetch user");
}
}
Quick Checks:
- ✅ Try-catch around await?
- ✅ HTTP status checked?
- ✅ Logged?
Error Handling Patterns
Pattern 1: Retry with Exponential Backoff
def call_api_with_retry(url, max_retries=3):
for attempt in range(max_retries):
try:
response = requests.get(url, timeout=5)
response.raise_for_status()
return response.json()
except requests.Timeout:
if attempt < max_retries - 1:
wait = 2 ** attempt # 1s, 2s, 4s
logger.warning(f"Timeout, retrying in {wait}s...")
time.sleep(wait)
else:
raise
When to use: Transient failures (network, rate limits)
Pattern 2: Fallback Values
def get_user_avatar(user_id):
try:
return fetch_from_cdn(user_id)
except CDNError:
logger.warning(f"CDN failed for user {user_id}, using default")
return DEFAULT_AVATAR_URL
When to use: Non-critical operations, graceful degradation
Pattern 3: Circuit Breaker
class CircuitBreaker:
def __init__(self, max_failures=5):
self.failures = 0
self.max_failures = max_failures
self.is_open = False
def call(self, func):
if self.is_open:
raise ServiceUnavailableError("Circuit breaker open")
try:
result = func()
self.failures = 0 # Reset on success
return result
except Exception as e:
self.failures += 1
if self.failures >= self.max_failures:
self.is_open = True
logger.error("Circuit breaker opened")
raise
When to use: Preventing cascading failures
Output Format
## Error Handling Report
**Status**: [✅ COMPLETE | ⚠️ GAPS FOUND]
---
### Missing Error Handling: 3
1. **[HIGH] No timeout on API call (api_client.py:45)**
- **Issue**: `requests.get()` has no timeout
- **Risk**: Indefinite hang if service slow
- **Fix**:
```python
response = requests.get(url, timeout=5)
```
2. **[HIGH] Unhandled JSON parse error (config.py:12)**
- **Issue**: `json.load()` not wrapped in try-catch
- **Risk**: Crash on malformed JSON
- **Fix**:
```python
try:
config = json.load(f)
except json.JSONDecodeError as e:
logger.error(f"Invalid JSON: {e}")
return DEFAULT_CONFIG
```
3. **[MEDIUM] Silent exception swallowing (db.py:89)**
- **Issue**: `except Exception: pass`
- **Risk**: Failures go unnoticed
- **Fix**: Log error or use specific exception
---
**Good Practices Found**: 2
- ✅ Database errors logged properly (db.py:34)
- ✅ Retry logic on payment API (payments.py:67)
---
**Next Steps**:
1. Add timeout to API calls (5 min)
2. Wrap JSON parsing in try-catch (2 min)
3. Remove silent exception handlers (3 min)
What This Skill Does NOT Do
❌ Catch every possible exception (too noisy) ❌ Force try-catch everywhere (only where needed) ❌ Replace integration tests ❌ Handle business logic errors (validation, etc.)
✅ DOES: Check critical error-prone operations (network, I/O, parsing)
Configuration
# Strict mode: check all functions
export LAZYDEV_ERROR_HANDLING_STRICT=1
# Disable error handling checks
export LAZYDEV_DISABLE_ERROR_CHECKS=1
Version: 1.0.0 Focus: External calls, I/O, parsing, async Speed: <2 seconds per file