Description
The onUserUpdate hook in packages/polar-betterauth/src/hooks/customer.ts fires on every user profile update and unconditionally sends email + name to customers.updateExternal, even when neither field changed.
When a user updates a non-Polar field (e.g. username, avatar, custom profile flags), the Polar API rejects the call with:
A customer with this email address already exists.
The error is caught and only logged (doesn't block the update), but it's noisy and unnecessary.
Reproduction
- Configure
createCustomerOnSignUp: true
- Sign up a user (Polar customer is created)
- Update any non-email field on the user profile (e.g. username)
- Observe the error in logs:
ERROR [Better Auth]: Polar customer update failed. Error: API error occurred:
{"detail":[{"loc":["body","email"],"msg":"A customer with this email address already exists.","type":"value_error"}]}
Root cause
onUserUpdate (source):
await options.client.customers.updateExternal({
externalId: user.id,
customerUpdateExternalID: {
email: user.email, // always sent, even if unchanged
name: user.name,
},
});
Two issues:
- The hook sends
email on every update regardless of what actually changed
- The Polar API (
PATCH /v1/customers/external/{id}) appears to run a uniqueness check on email that matches the customer's own existing email, rejecting an idempotent update
Suggested fix
The user.update.after hook in better-auth receives both the updated user and context. Ideally the hook should:
- Only send fields that actually changed to Polar, or
- Skip the Polar API call entirely when neither
email nor name changed
This is similar to the fix applied in #161 for onBeforeUserCreate, where a check-before-create guard was added.
Environment
@polar-sh/better-auth: latest
@polar-sh/sdk: latest
better-auth: latest
- Polar environment: sandbox (also reproducible in production)
Description
The
onUserUpdatehook inpackages/polar-betterauth/src/hooks/customer.tsfires on every user profile update and unconditionally sendsemail+nametocustomers.updateExternal, even when neither field changed.When a user updates a non-Polar field (e.g. username, avatar, custom profile flags), the Polar API rejects the call with:
The error is caught and only logged (doesn't block the update), but it's noisy and unnecessary.
Reproduction
createCustomerOnSignUp: trueRoot cause
onUserUpdate(source):Two issues:
emailon every update regardless of what actually changedPATCH /v1/customers/external/{id}) appears to run a uniqueness check onemailthat matches the customer's own existing email, rejecting an idempotent updateSuggested fix
The
user.update.afterhook in better-auth receives both the updated user and context. Ideally the hook should:emailnornamechangedThis is similar to the fix applied in #161 for
onBeforeUserCreate, where a check-before-create guard was added.Environment
@polar-sh/better-auth: latest@polar-sh/sdk: latestbetter-auth: latest