go-gorm/gorm · error

conflicting callback %s with after %s

Error message

conflicting callback %s with after %s

What it means

The mirror of the Before case: while sorting callbacks, if a callback that declared After(x) is already positioned in the sorted list BEFORE x (curIdx < sortedIdx), the constraints are contradictory and GORM returns fmt.Errorf("conflicting callback %s with after %s", c.name, c.after). Callback registration order plus After constraints made the requested order impossible.

Source

Thrown at callbacks.go:313

					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)
				}
			} else if idx := getRIndex(names, c.after); idx != -1 {
				// if after callback exists but haven't sorted
				// set after callback's before callback to current callback
				after := cs[idx]

				if after.before == "" {
					after.before = c.name
				}

				if err := sortCallback(after); err != nil {
					return err
				}

				if err := sortCallback(c); err != nil {
					return err
				}
			}

View on GitHub (pinned to 1d6ce99528)

Solutions

  1. Audit all registered constraints for the processor (names + before/after) and remove the contradiction.
  2. Anchor plugins to unambiguous distinct points (before gorm:create vs after gorm:before_create) instead of each other.
  3. Upgrade or patch the conflicting plugin.
  4. Initialize plugins in a deterministic order at startup and register each exactly once.

Example fix

// before
p1.Create().After("p2_hook").Register("p1_hook", f1)
p2.Create().After("p1_hook").Register("p2_hook", f2) // cycle

// after: both anchor to core, not each other
p1.Create().Before("gorm:create").Register("p1_hook", f1)
p2.Create().After("gorm:create").Register("p2_hook", f2)
Defensive patterns

Strategy: validation

Try / catch

if err := c.After("audit").Register("my_cb", fn); err != nil {
    if strings.Contains(err.Error(), "conflicting callback") {
        // flip to Before or target gorm:* anchor, then re-register
    }
    return err
}

Prevention

When it happens

Trigger: Registering a callback with .After("X") when X's own constraints (or prior registrations) already forced the callback to sort before X; common with multiple plugins chaining After constraints around the same anchor callback (e.g. gorm:create).

Common situations: Audit/soft-delete/outbox plugins all anchoring around gorm:create with mixed Before/After; plugin version bumps that reorder registrations; test suites re-registering callbacks on a shared global DB.

Related errors


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