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

  1. Read the full formatted string — the prefix is the library's contextual Message and the suffix is the original driver error.
  2. Use errors.Is/As or unwrap (ErrorDB stores Err) to test for specific driver errors instead of string matching.
  3. If the message is unhelpfully empty or duplicated, check how the ErrorDB was constructed upstream (EmptyMessage vs nil Err branches).
  4. 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

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


AI-assisted analysis of gofr-dev/gofr@187eb24962 (2026-09-01). Data as JSON: /api/errors/2173f5f98480a35a. Report an issue: GitHub.