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
- Audit all registered constraints for the processor (names + before/after) and remove the contradiction.
- Anchor plugins to unambiguous distinct points (before gorm:create vs after gorm:before_create) instead of each other.
- Upgrade or patch the conflicting plugin.
- 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
- Avoid mutual Before/After constraints between plugins - use one direction only.
- Keep a single plugin-init function so registration order is deterministic.
- On GORM/plugin upgrades, run the callback-ordering smoke test first.
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
- conflicting callback %s with before %s
- violates check constraint
- unsupported relationship
- record not found
- failed to get schema
AI-assisted analysis of go-gorm/gorm@1d6ce99528 (2026-08-15).
Data as JSON: /api/errors/998e62d371b08ab2.
Report an issue: GitHub.