go-gorm/gorm · error

conflicting callback %s with before %s

Error message

conflicting callback %s with before %s

What it means

When a processor sorts its callbacks (sortCallback in callbacks.go), each callback's Before constraint is checked against the already-sorted list. If the callback itself is already in the sorted list at a position AFTER the callback it declared Before (curIdx > sortedIdx), the ordering constraints are contradictory and GORM returns fmt.Errorf("conflicting callback %s with before %s", c.name, c.before).

Source

Thrown at callbacks.go:295

		// show warning message the callback name already exists
		if idx := getRIndex(names, c.name); idx > -1 && !c.replace && !c.remove && !cs[idx].remove {
			c.processor.db.Logger.Warn(context.Background(), "duplicated callback `%s` from %s\n", c.name, utils.FileWithLineNum())
		}
		names = append(names, c.name)
	}

	sortCallback = func(c *callback) error {
		if c.before != "" { // if defined before callback
			if c.before == "*" && len(sorted) > 0 {
				if curIdx := getRIndex(sorted, c.name); curIdx == -1 {
					sorted = append([]string{c.name}, sorted...)
				}
			} else if sortedIdx := getRIndex(sorted, c.before); sortedIdx != -1 {
				if curIdx := getRIndex(sorted, c.name); curIdx == -1 {
					// if before callback already sorted, append current callback just after it
					sorted = append(sorted[:sortedIdx], append([]string{c.name}, sorted[sortedIdx:]...)...)
				} else if curIdx > sortedIdx {
					return fmt.Errorf("conflicting callback %s with before %s", c.name, c.before)
				}
			} else if idx := getRIndex(names, c.before); idx != -1 {
				// if before callback exists
				cs[idx].after = c.name
			}
		}

		if c.after != "" { // if defined after callback
			if c.after == "*" && len(sorted) > 0 {
				if curIdx := getRIndex(sorted, c.name); curIdx == -1 {
					sorted = append(sorted, c.name)
				}
			} else if sortedIdx := getRIndex(sorted, c.after); sortedIdx != -1 {
				if curIdx := getRIndex(sorted, c.name); curIdx == -1 {
					// if after callback sorted, append current callback to last
					sorted = append(sorted, c.name)
				} else if curIdx < sortedIdx {
					return fmt.Errorf("conflicting callback %s with after %s", c.name, c.after)

View on GitHub (pinned to 1d6ce99528)

Solutions

  1. Print db.Callback().Create().Get(...) / the registered names+constraints and identify the mutually contradictory pair.
  2. Change one side's constraint (swap Before for After, or target a different anchor callback).
  3. Update the plugin(s) to versions with compatible constraint sets.
  4. In tests, use a fresh gorm.Session/DB or remove callbacks before re-registering with new constraints.

Example fix

// before: plugin A
c.Create().After("my_cb").Register("audit", fn)
// plugin B
c.Create().Before("audit").Register("my_cb", fn2) // contradictory

// after: single consistent direction
c.Create().Before("gorm:create").Register("my_cb", fn2)
c.Create().Before("gorm:create").Register("audit", fn)
Defensive patterns

Strategy: validation

Validate before calling

// startup self-check: register all callbacks, then force a sort by starting a session
func initCallbacks(db *gorm.DB) error {
    // ... register plugins ...
    if err := db.Callback().Create().Replace("gorm:create", db.Callback().Create().Register /* or keep */); err != nil { return err }
    return nil // sort errors surface on first registration; catch them here at boot
}

Try / catch

if err := c.Register("my_cb", fn); err != nil {
    if strings.Contains(err.Error(), "conflicting callback") {
        // adjust Before/After anchor and retry registration once
    }
    return err
}

Prevention

When it happens

Trigger: Registering callbacks via db.Callback().Create().Before("gorm:create").Register("my_cb", ...) where earlier registrations already placed my_cb after gorm:create (e.g. via another callback's After("my_cb") constraint), producing a cycle/contradiction; typically happens when multiple plugins register callbacks with mutual constraints.

Common situations: Two plugins (audit hooks, soft-delete, caching) each registering Before/After constraints that interact badly; upgrading a plugin that changed its constraint targets; re-registering a callback after Remove with different constraints in tests.

Related errors


AI-assisted analysis of go-gorm/gorm@1d6ce99528 (2026-08-15). Data as JSON: /api/errors/7a9066445fd4a5cb. Report an issue: GitHub.