{"record":{"id":"998e62d371b08ab2","repo":"go-gorm/gorm","slug":"conflicting-callback-s-with-after-s","errorCode":null,"errorMessage":"conflicting callback %s with after %s","messagePattern":"conflicting callback (.+?) with after (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"callbacks.go","lineNumber":313,"sourceCode":"\t\t\t\t\treturn fmt.Errorf(\"conflicting callback %s with before %s\", c.name, c.before)\n\t\t\t\t}\n\t\t\t} else if idx := getRIndex(names, c.before); idx != -1 {\n\t\t\t\t// if before callback exists\n\t\t\t\tcs[idx].after = c.name\n\t\t\t}\n\t\t}\n\n\t\tif c.after != \"\" { // if defined after callback\n\t\t\tif c.after == \"*\" && len(sorted) > 0 {\n\t\t\t\tif curIdx := getRIndex(sorted, c.name); curIdx == -1 {\n\t\t\t\t\tsorted = append(sorted, c.name)\n\t\t\t\t}\n\t\t\t} else if sortedIdx := getRIndex(sorted, c.after); sortedIdx != -1 {\n\t\t\t\tif curIdx := getRIndex(sorted, c.name); curIdx == -1 {\n\t\t\t\t\t// if after callback sorted, append current callback to last\n\t\t\t\t\tsorted = append(sorted, c.name)\n\t\t\t\t} else if curIdx < sortedIdx {\n\t\t\t\t\treturn fmt.Errorf(\"conflicting callback %s with after %s\", c.name, c.after)\n\t\t\t\t}\n\t\t\t} else if idx := getRIndex(names, c.after); idx != -1 {\n\t\t\t\t// if after callback exists but haven't sorted\n\t\t\t\t// set after callback's before callback to current callback\n\t\t\t\tafter := cs[idx]\n\n\t\t\t\tif after.before == \"\" {\n\t\t\t\t\tafter.before = c.name\n\t\t\t\t}\n\n\t\t\t\tif err := sortCallback(after); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\n\t\t\t\tif err := sortCallback(c); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}","sourceCodeStart":295,"sourceCodeEnd":331,"githubUrl":"https://github.com/go-gorm/gorm/blob/1d6ce99528060be18a42be09aca8d39efcb47f28/callbacks.go#L295-L331","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before\np1.Create().After(\"p2_hook\").Register(\"p1_hook\", f1)\np2.Create().After(\"p1_hook\").Register(\"p2_hook\", f2) // cycle\n\n// after: both anchor to core, not each other\np1.Create().Before(\"gorm:create\").Register(\"p1_hook\", f1)\np2.Create().After(\"gorm:create\").Register(\"p2_hook\", f2)","handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := c.After(\"audit\").Register(\"my_cb\", fn); err != nil {\n    if strings.Contains(err.Error(), \"conflicting callback\") {\n        // flip to Before or target gorm:* anchor, then re-register\n    }\n    return err\n}","preventionTips":["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."],"tags":["gorm","callbacks","hooks","plugin-conflict","startup"],"backgroundTag":null,"analyzedSha":"1d6ce99528060be18a42be09aca8d39efcb47f28","analyzedAt":"2026-08-15T13:01:23.433Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}