[GH-ISSUE #8653] Bug: databaseHooks.member.delete.after is never called when removing a member via the organization plugin #11152

Closed
opened 2026-04-13 07:30:59 -05:00 by GiteaMirror · 3 comments
Owner

Originally created by @heiwen on GitHub (Mar 17, 2026).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/8653

Originally assigned to: @ping-maxwell on GitHub.

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

Description

The databaseHooks API accepts a member.delete.after hook, but it is silently never invoked when a member is removed through the organization plugin's removeMember endpoint. No error is thrown — it fails silently.

Steps to Reproduce

betterAuth({
  databaseHooks: {
    member: {
      delete: {
        after: async (member) => {
          console.log("member deleted", member); // never logged
        },
      },
    },
  },
  plugins: [organization()],
});

Call authClient.organization.removeMember(...). The after hook never fires.

Current vs. Expected behavior

Expected Behavior

databaseHooks.member.delete.after fires after a member is deleted, consistent with how user and session hooks behave.

Actual Behavior

The hook is never called. No error is thrown.

What version of Better Auth are you using?

1.5.3

System info

{
  "system": {
    "platform": "darwin",
    "arch": "arm64",
    "version": "Darwin Kernel Version 25.2.0: Tue Nov 18 21:09:34 PST 2025; root:xnu-12377.61.12~1/RELEASE_ARM64_T8112",
    "release": "25.2.0",
    "cpuCount": 8,
    "cpuModel": "Apple M2",
    "totalMemory": "16.00 GB",
    "freeMemory": "0.48 GB"
  },
  "node": {
    "version": "v25.7.0",
    "env": "development"
  },
  "packageManager": {
    "name": "bun",
    "version": "1.3.10"
  },
  "frameworks": null,
  "databases": null,
  "betterAuth": {
    "version": "Unknown",
    "config": null,
    "error": "ENOENT: no such file or directory, open 'package.json'"
  }
}

Which area(s) are affected? (Select all that apply)

Backend

Auth config (if applicable)


Additional context

Potential Root Cause

plugins/organization/adapter.mjsdeleteMember() calls adapter.delete() directly (the raw adapter), bypassing the hook-aware deleteWithHooks() wrapper in db/with-hooks.mjs. All other hooks that work (e.g. user.create.after, session.create.before) go through the hooked adapter path.

Relevant line in adapter.mjs:

const member = await adapter.delete({   // ← raw adapter, hooks never run
  model: "member",
  where: [{ field: "id", value: memberId }]
});

This should use the deleteWithHooks equivalent so registered databaseHooks are respected consistently.

Workaround

Use the organizationHooks.afterRemoveMember callback on the organization() plugin instead, which is correctly wired up and documented:

organization({
  organizationHooks: {
    afterRemoveMember: async ({ member }) => {
      // your logic here
    },
  },
}),

However, this workaround has a notable limitation: the callback receives no database client. If the side effect requires database access (e.g. updating sessions), the only option is to close over an externally constructed client, which is not obvious from the API and forces users to manage their own db connection alongside better-auth's internal one:

// User must construct and manage their own prisma instance just to use this hook
const prisma = new PrismaClient();

organization({
  organizationHooks: {
    afterRemoveMember: async ({ member }) => {
      await prisma.sessions.updateMany({ ... }); // no db client provided by better-auth
    },
  },
}),

Ideally the hook would receive a db or context argument similar to how other parts of the better-auth API expose the internal adapter.

Originally created by @heiwen on GitHub (Mar 17, 2026). Original GitHub issue: https://github.com/better-auth/better-auth/issues/8653 Originally assigned to: @ping-maxwell on GitHub. ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce ## Description The `databaseHooks` API accepts a `member.delete.after` hook, but it is silently never invoked when a member is removed through the organization plugin's `removeMember` endpoint. No error is thrown — it fails silently. ## Steps to Reproduce ```ts betterAuth({ databaseHooks: { member: { delete: { after: async (member) => { console.log("member deleted", member); // never logged }, }, }, }, plugins: [organization()], }); ``` Call `authClient.organization.removeMember(...)`. The `after` hook never fires. ### Current vs. Expected behavior ## Expected Behavior `databaseHooks.member.delete.after` fires after a member is deleted, consistent with how `user` and `session` hooks behave. ## Actual Behavior The hook is never called. No error is thrown. ### What version of Better Auth are you using? 1.5.3 ### System info ```bash { "system": { "platform": "darwin", "arch": "arm64", "version": "Darwin Kernel Version 25.2.0: Tue Nov 18 21:09:34 PST 2025; root:xnu-12377.61.12~1/RELEASE_ARM64_T8112", "release": "25.2.0", "cpuCount": 8, "cpuModel": "Apple M2", "totalMemory": "16.00 GB", "freeMemory": "0.48 GB" }, "node": { "version": "v25.7.0", "env": "development" }, "packageManager": { "name": "bun", "version": "1.3.10" }, "frameworks": null, "databases": null, "betterAuth": { "version": "Unknown", "config": null, "error": "ENOENT: no such file or directory, open 'package.json'" } } ``` ### Which area(s) are affected? (Select all that apply) Backend ### Auth config (if applicable) ```typescript ``` ### Additional context ## Potential Root Cause `plugins/organization/adapter.mjs` → `deleteMember()` calls `adapter.delete()` directly (the raw adapter), bypassing the hook-aware `deleteWithHooks()` wrapper in `db/with-hooks.mjs`. All other hooks that work (e.g. `user.create.after`, `session.create.before`) go through the hooked adapter path. Relevant line in `adapter.mjs`: ```js const member = await adapter.delete({ // ← raw adapter, hooks never run model: "member", where: [{ field: "id", value: memberId }] }); ``` This should use the `deleteWithHooks` equivalent so registered `databaseHooks` are respected consistently. ## Workaround Use the `organizationHooks.afterRemoveMember` callback on the `organization()` plugin instead, which is correctly wired up and documented: ```ts organization({ organizationHooks: { afterRemoveMember: async ({ member }) => { // your logic here }, }, }), ``` However, this workaround has a notable limitation: the callback receives no database client. If the side effect requires database access (e.g. updating sessions), the only option is to close over an externally constructed client, which is not obvious from the API and forces users to manage their own db connection alongside better-auth's internal one: ```ts // User must construct and manage their own prisma instance just to use this hook const prisma = new PrismaClient(); organization({ organizationHooks: { afterRemoveMember: async ({ member }) => { await prisma.sessions.updateMany({ ... }); // no db client provided by better-auth }, }, }), ``` Ideally the hook would receive a `db` or `context` argument similar to how other parts of the better-auth API expose the internal adapter.
GiteaMirror added the bugorganization labels 2026-04-13 07:30:59 -05:00
Author
Owner

@GautamBytes commented on GitHub (Mar 17, 2026):

Looking into it!

<!-- gh-comment-id:4073397836 --> @GautamBytes commented on GitHub (Mar 17, 2026): Looking into it!
Author
Owner

@heiwen commented on GitHub (Mar 30, 2026):

Some additional context: I reported this bug for member.delete.after, but I think it also applies to member.create.before/after in the same way.

<!-- gh-comment-id:4158883925 --> @heiwen commented on GitHub (Mar 30, 2026): Some additional context: I reported this bug for `member.delete.after`, but I think it also applies to `member.create.before/after` in the same way.
Author
Owner

@ping-maxwell commented on GitHub (Mar 31, 2026):

@heiwen there isn't support for database hooks on any non-core tables. This isn't supported in the first place yet.

For now you can use plugin config provided hooks/events to determine state

<!-- gh-comment-id:4163122536 --> @ping-maxwell commented on GitHub (Mar 31, 2026): @heiwen there isn't support for database hooks on any non-core tables. This isn't supported in the first place yet. For now you can use plugin config provided hooks/events to determine state
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#11152