---
title: Do Not Return Generic Errors
impact: HIGH
impactDescription: enables proper error handling and monitoring
tags: error-handling, custom-errors, debugging, quality
---

## Do Not Return Generic Errors

Generic errors (like `errors.New("error")`) lack context needed for debugging. They make it impossible to distinguish between error types for proper handling.

**Incorrect (generic errors):**

```go
if user == nil {
	return errors.New("error")
}

if !isValid {
	return fmt.Errorf("invalid")
}
```

**Correct (specific custom errors or sentinel errors):**

```go
// Sentinel errors for simple checks
var ErrUserNotFound = errors.New("user not found")

func (s *Service) GetUser(id string) (*User, error) {
	if user == nil {
		return nil, fmt.Errorf("%w: user with ID %s not found", ErrUserNotFound, id)
	}
	return user, nil
}

// Custom error types for complex context
type ValidationError struct {
	Field   string
	Message string
	Value   any
}

func (e *ValidationError) Error() string {
	return fmt.Sprintf("validation failed on %s: %s", e.Field, e.Message)
}

if !isValid {
	return &ValidationError{
		Field:   "email",
		Message: "invalid format",
		Value:   email,
	}
}
```

Custom errors should include:
- Descriptive message with context (use `%w` for wrapping)
- Error codes or types for programmatic handling (using `errors.Is` or `errors.As`)
- Relevant data for debugging
- Appropriate mapping to status codes in the transport layer (e.g., HTTP 404)

**Tools:** GolangCI-Lint (errname, goerr113), Manual review
