gofr-dev/gofr · warning · ErrorDB
errors.Wrap(e.Err, e.Message).Error()
Error message
errors.Wrap(e.Err, e.Message).Error()
What it means
ErrorDB is gofr's structured database error type holding an underlying Err and a human-readable Message. Its Error() method switches on which fields are populated: empty Message returns just the inner error's text, nil Err returns just the message, and when both are set it delegates to github.com/pkg/errors.Wrap(e.Err, e.Message).Error(), producing "Message: inner error text". This is the string rendering path, hit whenever any ErrorDB is printed, logged, or compared.
Source
Thrown at pkg/gofr/datasource/errors.go:22
"net/http"
"github.com/pkg/errors"
)
// ErrorDB represents an error specific to database operations.
type ErrorDB struct {
Err error
Message string
}
func (e ErrorDB) Error() string {
switch {
case e.Message == "":
return e.Err.Error()
case e.Err == nil:
return e.Message
default:
return errors.Wrap(e.Err, e.Message).Error()
}
}
// WithStack adds a stack trace to the Error.
func (e ErrorDB) WithStack() ErrorDB {
e.Err = errors.WithStack(e.Err)
return e
}
func (ErrorDB) StatusCode() int {
return http.StatusInternalServerError
}
// ErrorRecordNotFound represents the scenario where no records are found in the DB for the given ID.
type ErrorRecordNotFound ErrorDB
// StatusCode implementation on the ErrorRecordNotFound is an aberration
// since the errors in datasource package should not have anything to do with HTTP status codes.View on GitHub (pinned to 187eb24962)
Solutions
- Read the full formatted string — the prefix is the library's contextual Message and the suffix is the original driver error.
- Use errors.Is/As or unwrap (ErrorDB stores Err) to test for specific driver errors instead of string matching.
- If the message is unhelpfully empty or duplicated, check how the ErrorDB was constructed upstream (EmptyMessage vs nil Err branches).
- Call WithStack() on the ErrorDB when you need a stack trace captured for debugging.
Example fix
// before
if err != nil {
if strings.Contains(err.Error(), "duplicate key") { ... }
}
// after
var errDB gofrErrorDB
if errors.As(err, &errDB) && errors.Is(errDB.Err, sql.ErrNoRows) {
// handle not-found precisely
} Defensive patterns
Strategy: type-guard
Type guard
func asErrorDB(err error) (msg string, cause error, ok bool) {
var edb datasource.ErrorDB
if errors.As(err, &edb) {
return edb.Error(), edb.Err, true
}
return "", nil, false
} Try / catch
if err != nil {
if cause := errors.Unwrap(err); cause != nil {
logger.Errorf("db op failed: %v (cause: %v)", err, cause)
} else {
logger.Errorf("db op failed: %v", err)
}
return err
} Prevention
- Match on unwrapped causes with errors.Is/errors.As instead of string comparisons on Error().
- Call WithStack() when capturing ErrorDB for diagnostics.
- When wrapping, provide a meaningful Message so logs are actionable.
- Test both wrap branches (Message+Err, Message only, Err only) in unit tests.
When it happens
Trigger: Rendering any ErrorDB where both Err and Message are non-empty — e.g. a datasource operation failing and being wrapped with context like "failed to fetch user" plus the driver error. The error itself is thrown by whatever populated the ErrorDB; this line only formats it.
Common situations: Logging DB query failures in service handlers; comparing err.Error() output in tests expecting a specific message format; wrapping SQL driver errors with business context; cascading errors where each layer adds its own message prefix.
Related errors
- %w: %w
- failed to generate signed URL for %q: %w
- unsupported db dialect; supported dialects are - mysql, post
- query error
- %w: %v
AI-assisted analysis of gofr-dev/gofr@187eb24962 (2026-09-01).
Data as JSON: /api/errors/2173f5f98480a35a.
Report an issue: GitHub.