Skills Error Handling and Fault Tolerance
A Skill that only runs under ideal conditions is fragile.
A robust Skill needs to anticipate common errors, provide clear feedback, and recover automatically whenever possible.
Three Levels of Error Handling
| Level | Location | Handling Method |
|---|---|---|
| Input Validation Errors | Before Execution | Check the input; if invalid, reject it directly and explain the reason |
| Runtime Errors | During Script Execution | Catch exceptions and output structured error information |
| Abnormal Results | After Execution | Verify whether the output meets expectations; if not, trigger the remediation process |
Defining Error Handling Strategies in SKILL.md
SKILL.md should include a clear error handling section that tells Claude what to do when encountering problems.
Example
### Input Issues
- File does not exist: inform the user that the file path is incorrect and ask them to re-upload
- File format not supported: list the supported formats (pdf, docx, txt) and ask the user to convert and retry
- File is empty: inform the user that the file content is empty and cannot be processed
### Execution Issues
- Script execution failed: show the complete error information to the user, do not hide it
- Dependencies not installed: first try automatic installation (pipinstall), if that fails, inform the user to install manually
- Timeout: if it exceeds60seconds without completing, output the current progress and ask the user whether to continue waiting
### Result Issues
- Output file is empty: re-check the processing logic and explain possible reasons to the user
- Output format does not meet expectations: show the actual output and ask the user if they are satisfied
Standard Error Handling Patterns in Scripts
Python scripts should use a try-except structure and uniformly return JSON containing a status field.
Example
import json
import sys
import os
class SkillError(Exception):
"""Skill custom exception base class"""
def __init__(self, message: str, code: str):
self.message = message
self.code = code # Error codes to help the program determine the error type
super().__init__(message)
class InputError(SkillError):
"""Input validation error"""
pass
class ProcessError(SkillError):
"""Processing error"""
pass
def validate_input(file_path: str) -> None:
"""Validate input, raise InputError on error"""
if not file_path:
raise InputError("No file path provided", "MISSING_FILE")
if not os.path.exists(file_path):
raise InputError(f"File does not exist: {file_path}", "FILE_NOT_FOUND")
if os.path.getsize(file_path) == 0:
raise InputError(f"File content is empty: {file_path}", "EMPTY_FILE")
def process(file_path: str) -> dict:
"""Main processing logic"""
try:
validate_input(file_path)
with open(file_path, "r", encoding="utf-8") as f:
content = f.read()
# Actual processing logic (simplified here to line counting)
lines = content.split("\n")
return {
"status": "success",
"data": {
"file": file_path,
"lines": len(lines),
"chars": len(content)
}
}
except InputError as e:
# Input error: the user can correct it and retry
return {
"status": "input_error",
"code": e.code,
"message": e.message,
"hint": "Please check whether the file path is correct, and whether the file exists and is not empty"
}
except UnicodeDecodeError:
# Encoding error: provide specific fix suggestions
return {
"status": "process_error",
"code": "ENCODING_ERROR",
"message": "The file encoding is not UTF-8 and cannot be read",
"hint": "Please save the file as UTF-8 encoded and retry"
}
except Exception as e:
# Unexpected error: record it completely for easier debugging
return {
"status": "unexpected_error",
"code": "UNKNOWN",
"message": str(e),
"hint": "Please provide the above error information to the developer"
}
if __name__ == "__main__":
file_path = sys.argv[1] if len(sys.argv) > 1 else ""
result = process(file_path)
print(json.dumps(result, ensure_ascii=False, indent=2))
# 文件存在时的成功输出:
{
"status": "success",
"data": {
"file": "/mnt/user-data/uploads/example.txt",
"lines": 128,
"chars": 5432
}
}
# 文件不存在时的错误输出:
{
"status": "input_error",
"code": "FILE_NOT_FOUND",
"message": "文件不存在:/mnt/user-data/uploads/missing.txt",
"hint": "请检查文件路径是否正确,文件是否存在且非空"
}
Fault Tolerance Pattern for Automatic Dependency Installation
When the Python packages that a script depends on are not installed, the script can automatically attempt to install them.
Example
import subprocess
import sys
def ensure_package(package_name: str, import_name: str = None) -> bool:
"""
Ensure the Python package is installed; if not, install it automatically
Parameters:
package_name: the package name used for pip installation (e.g., "python-docx")
import_name: the name used for import (e.g., "docx"), defaults to the same as package_name
"""
import_name = import_name or package_name
try:
__import__(import_name)
return True # Already installed
except ImportError:
print(f"Installing dependency: {package_name}...")
result = subprocess.run(
[sys.executable, "-m", "pip", "install", package_name,
"--break-system-packages", "--quiet"],
capture_output=True, text=True
)
if result.returncode == 0:
print(f"Installation successful: {package_name}")
return True
else:
print(f"Installation failed: {package_name}")
print(result.stderr)
return False
# Usage example: ensure all dependencies are ready at the beginning of the script
if not ensure_package("pandas"):
sys.exit(1)
if not ensure_package("openpyxl"):
sys.exit(1)
# After dependencies are ready, import normally
import pandas as pd
print("All dependencies are ready, starting execution...")
Automatic installation is only suitable for development and lightweight scenarios. In production Skills, dependencies should be documented and pre-installed by the user, rather than automatically installed at runtime.
Guidelines for Writing Error Messages
Good error messages help users quickly locate problems, while poor error messages only confuse people.
| Dimension | Poor Writing | Good Writing |
|---|---|---|
| Describe the problem | "An error occurred" | "The file report.csv does not exist in the upload directory" |
| Explain the reason | (None) | "The file name may be misspelled, or the file has not been uploaded yet" |
| Provide action | (None) | "Please re-upload the file, or check whether the file name is correct" |
Example
raise Exception("Error")
# Good error message
raise InputError(
"The file example_data.csv was not found in the upload directory."
"Possible reasons: the file name is misspelled, or the file has not been uploaded yet."
"Please re-upload the file and try again.",
code="FILE_NOT_FOUND"
)