This skill should be used when the user asks about "err113", "wrapcheck", "errorlint", "sentinel errors", "error wrapping", "errors.Is", "errors.As", "dynamic errors", or needs guidance on idiomatic Go error handling. Provides patterns for fixing error-related lint violations.
Provides Go error handling patterns for err113, wrapcheck, and errorlint linters. Triggers when users ask about these linters, sentinel errors, error wrapping, errors.Is/errors.As, or need guidance on idiomatic Go error handling.
/plugin marketplace add dkoosis/cc-plugins/plugin install go-error-hygiene@cc-pluginsThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Idiomatic Go error handling patterns for lint compliance and maintainability.
Three linters enforce Go error hygiene:
| Linter | Detects | Solution |
|---|---|---|
| err113 | errors.New() in return statements | Extract to sentinel errors |
| wrapcheck | Unwrapped errors from external packages | Wrap with fmt.Errorf("ctx: %w", err) |
| errorlint | err == ErrFoo comparisons | Use errors.Is(err, ErrFoo) |
Dynamic errors prevent callers from checking error types. Extract to package-level sentinels.
func Validate(s string) error {
if s == "" {
return errors.New("input is empty") // err113: dynamic error
}
if len(s) > 100 {
return errors.New("input too long") // err113: dynamic error
}
return nil
}
var (
ErrEmptyInput = errors.New("input is empty")
ErrInputTooLong = errors.New("input too long")
)
func Validate(s string) error {
if s == "" {
return ErrEmptyInput
}
if len(s) > 100 {
return ErrInputTooLong
}
return nil
}
ErrFoo for errors callers should checkerrFoo for internal errorsErrNotFound, not ErrError1Dynamic errors are acceptable when:
fmt.Errorf wrapping: fmt.Errorf("failed at %s: %w", loc, err)Add //nolint:err113 with justification for intentional dynamic errors.
External package errors must be wrapped to provide context for debugging.
func LoadUser(id string) (*User, error) {
row := db.QueryRow("SELECT * FROM users WHERE id = ?", id)
var u User
if err := row.Scan(&u.ID, &u.Name); err != nil {
return nil, err // wrapcheck: sql error not wrapped
}
return &u, nil
}
func LoadUser(id string) (*User, error) {
row := db.QueryRow("SELECT * FROM users WHERE id = ?", id)
var u User
if err := row.Scan(&u.ID, &u.Name); err != nil {
return nil, fmt.Errorf("load user %s: %w", id, err)
}
return &u, nil
}
Good:
return fmt.Errorf("parse config file: %w", err)
Bad:
return fmt.Errorf("error: %w", err) // Redundant "error"
return fmt.Errorf("parse config file: parse config file: %w", err) // Duplicated
return ErrNotFound is already descriptiveDirect comparison breaks with wrapped errors. Use errors.Is() and errors.As().
if err == sql.ErrNoRows { // errorlint: won't match wrapped error
return nil, ErrNotFound
}
if errors.Is(err, sql.ErrNoRows) {
return nil, ErrNotFound
}
// errors.Is: Check if error chain contains specific error
if errors.Is(err, os.ErrNotExist) {
// Handle missing file
}
// errors.As: Extract specific error type from chain
var pathErr *os.PathError
if errors.As(err, &pathErr) {
fmt.Printf("failed path: %s\n", pathErr.Path)
}
Before:
if e, ok := err.(*os.PathError); ok {
// ...
}
After:
var e *os.PathError
if errors.As(err, &e) {
// ...
}
| Violation | Pattern | Fix |
|---|---|---|
errors.New("msg") in return | err113 | Extract to var ErrFoo = errors.New("msg") |
return externalPkg.Func() error | wrapcheck | fmt.Errorf("ctx: %w", err) |
err == ErrFoo | errorlint | errors.Is(err, ErrFoo) |
err.(*Type) | errorlint | errors.As(err, &target) |
After fixes:
golangci-lint run --enable err113,wrapcheck,errorlint ./...
go test ./...
This skill should be used when the user asks to "create a hookify rule", "write a hook rule", "configure hookify", "add a hookify rule", or needs guidance on hookify rule syntax and patterns.
Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.