Prisma strictUndefinedChecks not working with username() plugin #1819

Closed
opened 2026-03-13 09:05:29 -05:00 by GiteaMirror · 2 comments
Owner

Originally created by @Jackman3005 on GitHub (Sep 1, 2025).

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

I am hitting an issue when Prisma's strictUndefinedChecks is enabled. I see in the debug logs that image is being filtered out, but not username and displayUsername.

Simple Curl

$ curl -X POST http://localhost:5002/api/auth/sign-up/email \
    -H "Content-Type: application/json" \
    -d '{
      "email": "test7@example.com",
      "password": "SecurePassword123!",
      "name": "Test User"
    }'
{"code":"FAILED_TO_CREATE_USER","message":"Failed to create user","details":{"name":"PrismaClientValidationError","clientVersion":"6.4.1"}}%

Debug logs showing issue

{"level":30,"time":1756715373627,"pid":39763,"hostname":"qm.home","msg":"Server listening at http://192.168.97.0:5002"}
{"level":30,"time":1756715381176,"pid":39763,"hostname":"qm.home","reqId":"req-1","req":{"method":"POST","url":"/api/auth/sign-up/email","host":"localhost:5002","remoteAddr
2025-09-01T08:29:41.189Z INFO [Better Auth]: [Prisma Adapter] #0 [1/3] findOne: {
  model: "User",
  where: [
    {
      operator: "eq",
      connector: "AND",
      field: "email",
      value: "test7@example.com",
    }
  ],
  select: undefined,
}
2025-09-01T08:29:41.212Z INFO [Better Auth]: [Prisma Adapter] #0 [2/3] findOne (DB Result): {
  model: "User",
  data: null,
}
2025-09-01T08:29:41.212Z INFO [Better Auth]: [Prisma Adapter] #0 [3/3] findOne (Parsed Result): {
  model: "User",
  data: null,
}
2025-09-01T08:29:41.329Z INFO [Better Auth]: [Prisma Adapter] #1 [1/4] create (Unsafe Input): {
  model: "User",
  data: {
    createdAt: 2025-09-01T08:29:41.329Z,
    updatedAt: 2025-09-01T08:29:41.329Z,
    emailVerified: false,
    email: "test7@example.com",
    name: "Test User",
    image: undefined,
    username: undefined,
    displayUsername: undefined,
  },
}
2025-09-01T08:29:41.330Z INFO [Better Auth]: [Prisma Adapter] #1 [2/4] create (Parsed Input): {
  model: "User",
  data: {
    name: "Test User",
    email: "test7@example.com",
    emailVerified: false,
    createdAt: 2025-09-01T08:29:41.329Z,
    updatedAt: 2025-09-01T08:29:41.329Z,
    username: undefined,
    displayUsername: undefined,
  },
}
{"level":30,"time":1756715381338,"pid":39763,"hostname":"qm.home","reqId":"req-1","res":{"statusCode":422},"responseTime":161.23312499999884,"msg":"request completed"}

Current vs. Expected behavior

I can't really remove strictUndefinedChecks setting on Prisma, because the generated client then no longer has Prisma.skip which would require a much larger refactor throughout our codebase, and we like this prisma feature, so not interested.

Workaround

I was able to get things working with the following "hack" that others may find useful. Since I'm creating a fresh prisma client just for the better-auth integration, I can extend that client to manually filter out the undefined values and replace them with skip. It could be cleaner I'm sure, but I think this should be fine for now. Maybe it helps someone else as well..

import { betterAuth } from "better-auth";
import { prismaAdapter } from "better-auth/adapters/prisma";
import { PrismaClient, Prisma } from "@prisma/client";
import { username } from "better-auth/plugins";

const _prisma = new PrismaClient();


function skipUndefinedValues<D extends Record<string, unknown>>(data: D): D {
  return Object.entries(data).reduce(
    (acc, [key, value]) => {
      acc[key as keyof typeof data] = Prisma.skip.ifUndefined(value) as any;
      return acc;
    },
    {} as typeof data,
  );
}

const isPrismaSkip = (val: unknown): val is typeof Prisma.skip => {
  // Prisma.skip appears to be a singleton in the source code. 
  // However, this is relying on internal implementation which could change. 
  // The `isSkip` function is not exposed for our use, unfortunately.
  return Prisma.skip === val;
};

const prismaClientWithUndefinedSkipFix = _prisma.$extends({
  query: {
    $allModels: {
      async create({ args, query }) {
        if (args.data) {
          if (!isPrismaSkip(args.data)) {
            args.data = skipUndefinedValues(args.data);
          }
        }
        return query(args);
      },
      async update({ args, query }) {
        if (args.data) {
          if (!isPrismaSkip(args.data)) {
            args.data = skipUndefinedValues(args.data);
          }
        }
        return query(args);
      },
      async upsert({ args, query }) {
        if (args.create) {
          if (!isPrismaSkip(args.create)) {
            args.create = skipUndefinedValues(args.create);
          }
        }
        if (args.update) {
          if (!isPrismaSkip(args.update)) {
            args.update = skipUndefinedValues(args.update);
          }
        }
        return query(args);
      },
    },
  },
});

export const auth = betterAuth({
  database: prismaAdapter(prismaClientWithUndefinedSkipFix, {
    provider: "postgresql",
    debugLogs: true,
  }),
...
});

What version of Better Auth are you using?

1.3.7

System info

System:
    OS: macOS 15.6.1
    CPU: (10) arm64 Apple M1 Pro
    Memory: 117.73 MB / 16.00 GB
    Shell: 5.9 - /bin/zsh

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

Backend

Auth config (if applicable)

export const auth = betterAuth({
  database: prismaAdapter(prisma, {
    provider: "postgresql",
    debugLogs: true,
  }),
  emailAndPassword: {
    enabled: true,
  },
  user: {
    modelName: "User",
  },
  plugins: [username()],
  baseURL: process.env.BETTER_AUTH_URL,
  advanced: {
    database: {
      useNumberId: true,
    },
  },
});
Originally created by @Jackman3005 on GitHub (Sep 1, 2025). ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce I am hitting an issue when Prisma's `strictUndefinedChecks` is enabled. I see in the debug logs that `image` is being filtered out, but not `username` and `displayUsername`. #### Simple Curl ``` $ curl -X POST http://localhost:5002/api/auth/sign-up/email \ -H "Content-Type: application/json" \ -d '{ "email": "test7@example.com", "password": "SecurePassword123!", "name": "Test User" }' {"code":"FAILED_TO_CREATE_USER","message":"Failed to create user","details":{"name":"PrismaClientValidationError","clientVersion":"6.4.1"}}% ``` #### Debug logs showing issue ``` {"level":30,"time":1756715373627,"pid":39763,"hostname":"qm.home","msg":"Server listening at http://192.168.97.0:5002"} {"level":30,"time":1756715381176,"pid":39763,"hostname":"qm.home","reqId":"req-1","req":{"method":"POST","url":"/api/auth/sign-up/email","host":"localhost:5002","remoteAddr 2025-09-01T08:29:41.189Z INFO [Better Auth]: [Prisma Adapter] #0 [1/3] findOne: { model: "User", where: [ { operator: "eq", connector: "AND", field: "email", value: "test7@example.com", } ], select: undefined, } 2025-09-01T08:29:41.212Z INFO [Better Auth]: [Prisma Adapter] #0 [2/3] findOne (DB Result): { model: "User", data: null, } 2025-09-01T08:29:41.212Z INFO [Better Auth]: [Prisma Adapter] #0 [3/3] findOne (Parsed Result): { model: "User", data: null, } 2025-09-01T08:29:41.329Z INFO [Better Auth]: [Prisma Adapter] #1 [1/4] create (Unsafe Input): { model: "User", data: { createdAt: 2025-09-01T08:29:41.329Z, updatedAt: 2025-09-01T08:29:41.329Z, emailVerified: false, email: "test7@example.com", name: "Test User", image: undefined, username: undefined, displayUsername: undefined, }, } 2025-09-01T08:29:41.330Z INFO [Better Auth]: [Prisma Adapter] #1 [2/4] create (Parsed Input): { model: "User", data: { name: "Test User", email: "test7@example.com", emailVerified: false, createdAt: 2025-09-01T08:29:41.329Z, updatedAt: 2025-09-01T08:29:41.329Z, username: undefined, displayUsername: undefined, }, } {"level":30,"time":1756715381338,"pid":39763,"hostname":"qm.home","reqId":"req-1","res":{"statusCode":422},"responseTime":161.23312499999884,"msg":"request completed"} ``` ### Current vs. Expected behavior I can't really remove `strictUndefinedChecks` setting on Prisma, because the generated client then no longer has `Prisma.skip` which would require a much larger refactor throughout our codebase, and we like this prisma feature, so not interested. # Workaround I was able to get things working with the following "hack" that others may find useful. Since I'm creating a fresh prisma client just for the better-auth integration, I can extend that client to manually filter out the `undefined` values and replace them with `skip`. It could be cleaner I'm sure, but I think this should be fine for now. Maybe it helps someone else as well.. ```typescript import { betterAuth } from "better-auth"; import { prismaAdapter } from "better-auth/adapters/prisma"; import { PrismaClient, Prisma } from "@prisma/client"; import { username } from "better-auth/plugins"; const _prisma = new PrismaClient(); function skipUndefinedValues<D extends Record<string, unknown>>(data: D): D { return Object.entries(data).reduce( (acc, [key, value]) => { acc[key as keyof typeof data] = Prisma.skip.ifUndefined(value) as any; return acc; }, {} as typeof data, ); } const isPrismaSkip = (val: unknown): val is typeof Prisma.skip => { // Prisma.skip appears to be a singleton in the source code. // However, this is relying on internal implementation which could change. // The `isSkip` function is not exposed for our use, unfortunately. return Prisma.skip === val; }; const prismaClientWithUndefinedSkipFix = _prisma.$extends({ query: { $allModels: { async create({ args, query }) { if (args.data) { if (!isPrismaSkip(args.data)) { args.data = skipUndefinedValues(args.data); } } return query(args); }, async update({ args, query }) { if (args.data) { if (!isPrismaSkip(args.data)) { args.data = skipUndefinedValues(args.data); } } return query(args); }, async upsert({ args, query }) { if (args.create) { if (!isPrismaSkip(args.create)) { args.create = skipUndefinedValues(args.create); } } if (args.update) { if (!isPrismaSkip(args.update)) { args.update = skipUndefinedValues(args.update); } } return query(args); }, }, }, }); export const auth = betterAuth({ database: prismaAdapter(prismaClientWithUndefinedSkipFix, { provider: "postgresql", debugLogs: true, }), ... }); ``` ### What version of Better Auth are you using? 1.3.7 ### System info ```bash System: OS: macOS 15.6.1 CPU: (10) arm64 Apple M1 Pro Memory: 117.73 MB / 16.00 GB Shell: 5.9 - /bin/zsh ``` ### Which area(s) are affected? (Select all that apply) Backend ### Auth config (if applicable) ```typescript export const auth = betterAuth({ database: prismaAdapter(prisma, { provider: "postgresql", debugLogs: true, }), emailAndPassword: { enabled: true, }, user: { modelName: "User", }, plugins: [username()], baseURL: process.env.BETTER_AUTH_URL, advanced: { database: { useNumberId: true, }, }, }); ```
GiteaMirror added the adapter label 2026-03-13 09:05:29 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Sep 1, 2025):

The issue comes from how Better Auth's adapter and the username() plugin handle optional fields when Prisma's strictUndefinedChecks is enabled. The plugin only includes username and displayUsername in the data if they're truthy, but the adapter's generic filtering logic doesn't always remove undefined for plugin-added fields—so these fields can end up as undefined in the Prisma input, which strict mode rejects. This is why image is filtered out but username and displayUsername are not, as seen in your debug logs.

Your workaround using Prisma's $extends and Prisma.skip is a solid approach and currently the best way to ensure all undefined values are handled correctly before hitting Prisma. There isn't a cleaner or built-in solution in Better Auth for this scenario right now, and no existing tickets or docs cover this specific integration issue.

For now, keep using your workaround or a similar pattern. It would be beneficial for Better Auth to natively support Prisma.skip for all optional fields, including those added by plugins, to make strict mode integration seamless. If this is a blocker for you or others, it's worth surfacing as feedback to the maintainers for future improvements.

References:

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Chat with Dosu Join Discord Share on X

@dosubot[bot] commented on GitHub (Sep 1, 2025): <!-- Answer --> The issue comes from how Better Auth's adapter and the `username()` plugin handle optional fields when Prisma's `strictUndefinedChecks` is enabled. The plugin only includes `username` and `displayUsername` in the data if they're truthy, but the adapter's generic filtering logic doesn't always remove `undefined` for plugin-added fields—so these fields can end up as `undefined` in the Prisma input, which strict mode rejects. This is why `image` is filtered out but `username` and `displayUsername` are not, as seen in your debug logs. Your workaround using Prisma's `$extends` and `Prisma.skip` is a solid approach and currently the best way to ensure all `undefined` values are handled correctly before hitting Prisma. There isn't a cleaner or built-in solution in Better Auth for this scenario right now, and no existing tickets or docs cover this specific integration issue. For now, keep using your workaround or a similar pattern. It would be beneficial for Better Auth to natively support `Prisma.skip` for all optional fields, including those added by plugins, to make strict mode integration seamless. If this is a blocker for you or others, it's worth surfacing as feedback to the maintainers for future improvements. References: - [username() plugin conditional inclusion logic](https://github.com/better-auth/better-auth/blob/cf7f2f118147b6a79a99fcf48898408f038390f5/packages/better-auth/src/plugins/username/index.ts) - [Adapter's generic filtering of undefined values](https://github.com/better-auth/better-auth/blob/cf7f2f118147b6a79a99fcf48898408f038390f5/packages/better-auth/src/adapters/create-adapter/index.ts) <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/f730b03f-cf11-4be2-8a41-ed4aa353cef9?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/f730b03f-cf11-4be2-8a41-ed4aa353cef9?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/f730b03f-cf11-4be2-8a41-ed4aa353cef9?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/f730b03f-cf11-4be2-8a41-ed4aa353cef9?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/f730b03f-cf11-4be2-8a41-ed4aa353cef9?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/f730b03f-cf11-4be2-8a41-ed4aa353cef9?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/f730b03f-cf11-4be2-8a41-ed4aa353cef9?feedback_type=other)</sup>&nbsp;&nbsp;[![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/4341)
Author
Owner

@Jackman3005 commented on GitHub (Sep 1, 2025):

I did see this issue which has had a PR to resolve the Prisma strictUndefinedChecks situation. However, it does not appear to apply to plugins? I'm not really sure, didn't dig in too much deeper.

@Jackman3005 commented on GitHub (Sep 1, 2025): I did see [this issue](https://github.com/better-auth/better-auth/issues/873) which has had a PR to resolve the Prisma `strictUndefinedChecks` situation. However, it does not appear to apply to plugins? I'm not really sure, didn't dig in too much deeper.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#1819