[GH-ISSUE #6886] Missing addicional fields for user on emailVerification #19296

Closed
opened 2026-04-15 18:12:07 -05:00 by GiteaMirror · 2 comments
Owner

Originally created by @douglasndm on GitHub (Dec 19, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/6886

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

Try to use an additional field on emailVerification function. In this case user.preferredLanguage is available to use but Typescript complain about it telling that is does not exists

Current vs. Expected behavior

Currently: Typescript tells that property does not exists but the value is correctly present.

Expected: The value continue to be present and Typescript stops complain about this

What version of Better Auth are you using?

1.4.7

System info

{
  "system": {
    "platform": "win32",
    "arch": "x64",
    "version": "Windows 11 Pro",
    "release": "10.0.26200",
    "cpuCount": 16,
    "cpuModel": "AMD Ryzen 7 7800X3D 8-Core Processor           ",
    "totalMemory": "31.64 GB",
    "freeMemory": "7.10 GB"
  },
  "node": {
    "version": "v24.11.1",
    "env": "development"
  },
  "packageManager": {
    "name": "npm",
    "version": "11.6.2"
  },
  "frameworks": [
    {
      "name": "express",
      "version": "^5.1.0"
    }
  ],
  "databases": [
    {
      "name": "pg",
      "version": "^8.16.3"
    }
  ],
  "betterAuth": {
    "version": "^1.4.7",
    "config": {
      "emailAndPassword": {
        "enabled": true,
        "revokeSessionsOnPasswordReset": true
      },
      "emailVerification": {
        "sendOnSignUp": true
      },
      "advanced": {
        "database": {
          "generateId": "uuid"
        }
      },
      "user": {
        "additionalFields": {
          "firebaseUid": {
            "type": "string",
            "required": false
          },
          "internalUserId": {
            "type": "string",
            "required": false
          },
          "preferredLanguage": {
            "type": "string",
            "required": false,
            "defaultValue": "pt-BR"
          }
        }
      },
      "plugins": [
        {
          "name": "admin",
          "config": {
            "id": "admin",
            "hooks": {
              "after": [
                {}
              ]
            },
            "endpoints": {},
            "$ERROR_CODES": {
              "FAILED_TO_CREATE_USER": "Failed to create user",
              "USER_ALREADY_EXISTS": "User already exists.",
              "USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL": "User already exists. Use another email.",
              "YOU_CANNOT_BAN_YOURSELF": "You cannot ban yourself",
              "YOU_ARE_NOT_ALLOWED_TO_CHANGE_USERS_ROLE": "You are not allowed to change users role",
              "YOU_ARE_NOT_ALLOWED_TO_CREATE_USERS": "You are not allowed to create users",
              "YOU_ARE_NOT_ALLOWED_TO_LIST_USERS": "You are not allowed to list users",
              "YOU_ARE_NOT_ALLOWED_TO_LIST_USERS_SESSIONS": "You are not allowed to list users sessions",
              "YOU_ARE_NOT_ALLOWED_TO_BAN_USERS": "You are not allowed to ban users",
              "YOU_ARE_NOT_ALLOWED_TO_IMPERSONATE_USERS": "You are not allowed to impersonate users",
              "YOU_ARE_NOT_ALLOWED_TO_REVOKE_USERS_SESSIONS": "You are not allowed to revoke users sessions",
              "YOU_ARE_NOT_ALLOWED_TO_DELETE_USERS": "You are not allowed to delete users",
              "YOU_ARE_NOT_ALLOWED_TO_SET_USERS_PASSWORD": "[REDACTED]",
              "BANNED_USER": "You have been banned from this application",
              "YOU_ARE_NOT_ALLOWED_TO_GET_USER": "You are not allowed to get user",
              "NO_DATA_TO_UPDATE": "No data to update",
              "YOU_ARE_NOT_ALLOWED_TO_UPDATE_USERS": "You are not allowed to update users",
              "YOU_CANNOT_REMOVE_YOURSELF": "You cannot remove yourself",
              "YOU_ARE_NOT_ALLOWED_TO_SET_NON_EXISTENT_VALUE": "You are not allowed to set a non-existent role value",
              "YOU_CANNOT_IMPERSONATE_ADMINS": "You cannot impersonate admins",
              "INVALID_ROLE_TYPE": "Invalid role type"
            },
            "schema": {
              "user": {
                "fields": {
                  "role": {
                    "type": "string",
                    "required": false,
                    "input": false
                  },
                  "banned": {
                    "type": "boolean",
                    "defaultValue": false,
                    "required": false,
                    "input": false
                  },
                  "banReason": {
                    "type": "string",
                    "required": false,
                    "input": false
                  },
                  "banExpires": {
                    "type": "date",
                    "required": false,
                    "input": false
                  }
                }
              },
              "session": {
                "fields": {
                  "impersonatedBy": {
                    "type": "string",
                    "required": false
                  }
                }
              }
            },
            "options": {
              "adminRoles": [
                "admin"
              ],
            }
          }
        },
        {
          "name": "bearer",
          "config": {
            "id": "bearer",
            "hooks": {
              "before": [
                {}
              ],
              "after": [
                {}
              ]
            }
          }
        },
        {
          "name": "expo",
          "config": {
            "id": "expo",
            "hooks": {
              "after": [
                {}
              ]
            },
            "endpoints": {}
          }
        },
        {
          "name": "magic-link",
          "config": {
            "id": "magic-link",
            "endpoints": {},
            "rateLimit": [
              {
                "window": 60,
                "max": 5
              }
            ]
          }
        }
      ]
    }
  }
}

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

Types

Auth config (if applicable)

emailVerification: {
		sendOnSignUp: true,
		sendVerificationEmail: async ({ user, token }) => {
			await sendBaseAppConfirmationEmail({
				email: user.email,
				name: user.name,
				token,
				language: user.preferredLanguage,
			});
		},
	},
user: {
		additionalFields: {
			preferredLanguage: {
				type: 'string',
				required: false,
			},
		},
	},

Additional context

This value of additional fields are present and I can use it, but typescript still tells that it does not exists

Originally created by @douglasndm on GitHub (Dec 19, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/6886 ### Is this suited for github? - [ ] Yes, this is suited for github ### To Reproduce Try to use an additional field on emailVerification function. In this case user.preferredLanguage is available to use but Typescript complain about it telling that is does not exists ### Current vs. Expected behavior Currently: Typescript tells that property does not exists but the value is correctly present. Expected: The value continue to be present and Typescript stops complain about this ### What version of Better Auth are you using? 1.4.7 ### System info ```bash { "system": { "platform": "win32", "arch": "x64", "version": "Windows 11 Pro", "release": "10.0.26200", "cpuCount": 16, "cpuModel": "AMD Ryzen 7 7800X3D 8-Core Processor ", "totalMemory": "31.64 GB", "freeMemory": "7.10 GB" }, "node": { "version": "v24.11.1", "env": "development" }, "packageManager": { "name": "npm", "version": "11.6.2" }, "frameworks": [ { "name": "express", "version": "^5.1.0" } ], "databases": [ { "name": "pg", "version": "^8.16.3" } ], "betterAuth": { "version": "^1.4.7", "config": { "emailAndPassword": { "enabled": true, "revokeSessionsOnPasswordReset": true }, "emailVerification": { "sendOnSignUp": true }, "advanced": { "database": { "generateId": "uuid" } }, "user": { "additionalFields": { "firebaseUid": { "type": "string", "required": false }, "internalUserId": { "type": "string", "required": false }, "preferredLanguage": { "type": "string", "required": false, "defaultValue": "pt-BR" } } }, "plugins": [ { "name": "admin", "config": { "id": "admin", "hooks": { "after": [ {} ] }, "endpoints": {}, "$ERROR_CODES": { "FAILED_TO_CREATE_USER": "Failed to create user", "USER_ALREADY_EXISTS": "User already exists.", "USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL": "User already exists. Use another email.", "YOU_CANNOT_BAN_YOURSELF": "You cannot ban yourself", "YOU_ARE_NOT_ALLOWED_TO_CHANGE_USERS_ROLE": "You are not allowed to change users role", "YOU_ARE_NOT_ALLOWED_TO_CREATE_USERS": "You are not allowed to create users", "YOU_ARE_NOT_ALLOWED_TO_LIST_USERS": "You are not allowed to list users", "YOU_ARE_NOT_ALLOWED_TO_LIST_USERS_SESSIONS": "You are not allowed to list users sessions", "YOU_ARE_NOT_ALLOWED_TO_BAN_USERS": "You are not allowed to ban users", "YOU_ARE_NOT_ALLOWED_TO_IMPERSONATE_USERS": "You are not allowed to impersonate users", "YOU_ARE_NOT_ALLOWED_TO_REVOKE_USERS_SESSIONS": "You are not allowed to revoke users sessions", "YOU_ARE_NOT_ALLOWED_TO_DELETE_USERS": "You are not allowed to delete users", "YOU_ARE_NOT_ALLOWED_TO_SET_USERS_PASSWORD": "[REDACTED]", "BANNED_USER": "You have been banned from this application", "YOU_ARE_NOT_ALLOWED_TO_GET_USER": "You are not allowed to get user", "NO_DATA_TO_UPDATE": "No data to update", "YOU_ARE_NOT_ALLOWED_TO_UPDATE_USERS": "You are not allowed to update users", "YOU_CANNOT_REMOVE_YOURSELF": "You cannot remove yourself", "YOU_ARE_NOT_ALLOWED_TO_SET_NON_EXISTENT_VALUE": "You are not allowed to set a non-existent role value", "YOU_CANNOT_IMPERSONATE_ADMINS": "You cannot impersonate admins", "INVALID_ROLE_TYPE": "Invalid role type" }, "schema": { "user": { "fields": { "role": { "type": "string", "required": false, "input": false }, "banned": { "type": "boolean", "defaultValue": false, "required": false, "input": false }, "banReason": { "type": "string", "required": false, "input": false }, "banExpires": { "type": "date", "required": false, "input": false } } }, "session": { "fields": { "impersonatedBy": { "type": "string", "required": false } } } }, "options": { "adminRoles": [ "admin" ], } } }, { "name": "bearer", "config": { "id": "bearer", "hooks": { "before": [ {} ], "after": [ {} ] } } }, { "name": "expo", "config": { "id": "expo", "hooks": { "after": [ {} ] }, "endpoints": {} } }, { "name": "magic-link", "config": { "id": "magic-link", "endpoints": {}, "rateLimit": [ { "window": 60, "max": 5 } ] } } ] } } } ``` ### Which area(s) are affected? (Select all that apply) Types ### Auth config (if applicable) ```typescript emailVerification: { sendOnSignUp: true, sendVerificationEmail: async ({ user, token }) => { await sendBaseAppConfirmationEmail({ email: user.email, name: user.name, token, language: user.preferredLanguage, }); }, }, user: { additionalFields: { preferredLanguage: { type: 'string', required: false, }, }, }, ``` ### Additional context This value of additional fields are present and I can use it, but typescript still tells that it does not exists
GiteaMirror added the lockedenhancement labels 2026-04-15 18:12:08 -05:00
Author
Owner

@better-auth-agent[bot] commented on GitHub (Dec 19, 2025):

It seems like you're encountering a TypeScript error where it reports that the property preferredLanguage does not exist, even though it is defined in your configuration under additionalFields.

This issue often arises when TypeScript doesn't properly infer types for dynamic properties like those defined in additionalFields. Here are a few steps you can take to troubleshoot and potentially resolve the issue:

  1. Ensure TypeScript Configuration: Make sure you are using strict mode in your TypeScript configuration. You can set strict: true in your tsconfig.json file to enforce strict type checking.

  2. Type Inference: If additionalFields are set correctly but not being inferred, you might need to explicitly define your user type or use type assertions where you access preferredLanguage. For example:

    interface User {
       preferredLanguage?: string; // If you expect this field to be optional
    }
    
    const user: User = {...}; // get your user object
    console.log(user.preferredLanguage); // Should not throw an error now
    
  3. Check Usage Context: If the property is defined but you're accessing it in a context where types might not be inferred correctly (such as in a callback), consider moving the logic to a place where TypeScript can see the complete type definition for the user object.

  4. Look for Existing Issues: Check if there are any existing GitHub issues related to TypeScript errors with additionalFields. You might find others have faced similar issues, and there might be shared solutions or workarounds.

  5. Engage with the Community: If the issue persists, consider tagging the Better Auth team members @bekacru and @ping__ in your GitHub issue for direct assistance.

Would you like more specific guidance on setting up your TypeScript configuration, or any specific examples?

If you need more help, tag @better-auth-agent in a comment so I can respond.

DiagramDiscordGitHub

Diagram Join Star

<!-- gh-comment-id:3676735665 --> @better-auth-agent[bot] commented on GitHub (Dec 19, 2025): It seems like you're encountering a TypeScript error where it reports that the property `preferredLanguage` does not exist, even though it is defined in your configuration under `additionalFields`. This issue often arises when TypeScript doesn't properly infer types for dynamic properties like those defined in `additionalFields`. Here are a few steps you can take to troubleshoot and potentially resolve the issue: 1. **Ensure TypeScript Configuration**: Make sure you are using strict mode in your TypeScript configuration. You can set `strict: true` in your `tsconfig.json` file to enforce strict type checking. 2. **Type Inference**: If `additionalFields` are set correctly but not being inferred, you might need to explicitly define your user type or use type assertions where you access `preferredLanguage`. For example: ```typescript interface User { preferredLanguage?: string; // If you expect this field to be optional } const user: User = {...}; // get your user object console.log(user.preferredLanguage); // Should not throw an error now ``` 3. **Check Usage Context**: If the property is defined but you're accessing it in a context where types might not be inferred correctly (such as in a callback), consider moving the logic to a place where TypeScript can see the complete type definition for the user object. 4. **Look for Existing Issues**: Check if there are any existing GitHub issues related to TypeScript errors with `additionalFields`. You might find others have faced similar issues, and there might be shared solutions or workarounds. 5. **Engage with the Community**: If the issue persists, consider tagging the Better Auth team members @bekacru and @ping__ in your GitHub issue for direct assistance. Would you like more specific guidance on setting up your TypeScript configuration, or any specific examples? _If you need more help, tag @better-auth-agent in a comment so I can respond._ <!-- bot:webhook reply v1 --> [Diagram](https://repodiagrams.s3.eu-north-1.amazonaws.com/skyvern_ultra_detailed_interactive.html) • [Discord](https://discord.gg/fG2XXEuQX3) • [GitHub](https://github.com/Skyvern-AI/Skyvern) [![Diagram](https://img.shields.io/badge/Diagram-2b3137?style=flat-square)](https://repodiagrams.s3.eu-north-1.amazonaws.com/skyvern_ultra_detailed_interactive.html) [![Join](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&style=flat-square)](https://discord.gg/fG2XXEuQX3) [![Star](https://img.shields.io/badge/star-181717?logo=github&logoColor=white&style=flat-square)](https://github.com/Skyvern-AI/Skyvern)
Author
Owner

@dosubot[bot] commented on GitHub (Dec 19, 2025):

Hi @douglasndm! I'm Dosu and I’m helping the better-auth team.

TypeScript complains about user.preferredLanguage in emailVerification because Better Auth’s type system doesn’t automatically merge your additionalFields into the User type used in sendVerificationEmail. This is a known limitation: the runtime user object includes your extra fields, but the type definition does not, so TypeScript flags them as missing even though they’re present at runtime (example, another example).

The recommended workaround is to explicitly cast the user object to a type that includes your additional fields. For example:

import { auth } from "./your-auth-config-file";

emailVerification: {
  sendVerificationEmail: async ({ user, token }) => {
    const u = user as typeof auth.$Infer.Session.user;
    await sendBaseAppConfirmationEmail({
      email: u.email,
      name: u.name,
      token,
      language: u.preferredLanguage, // No TS error now
    });
  },
}

This tells TypeScript to treat user as your full inferred user type, including all additionalFields. You can also define your own interface if you prefer.

Recent versions of Better Auth have improved type inference for additionalFields in some API responses and plugins, but as of 1.4.7, explicit casting is still required in hooks like sendVerificationEmail (source, source). There’s ongoing work to make this more seamless in the future.

Setting input: true or returned: true on your additionalFields can help with validation and API responses, but it does not affect the type inference in sendVerificationEmail.

Let me know if you want a more detailed example or have questions about extending types elsewhere!

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other

Chat with Dosu Open in Cursor Join Discord Share on X

<!-- gh-comment-id:3676746172 --> @dosubot[bot] commented on GitHub (Dec 19, 2025): <!-- Greeting --> Hi @douglasndm! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. <!-- Answer --> TypeScript complains about user.preferredLanguage in emailVerification because Better Auth’s type system doesn’t automatically merge your additionalFields into the User type used in sendVerificationEmail. This is a known limitation: the runtime user object includes your extra fields, but the type definition does not, so TypeScript flags them as missing even though they’re present at runtime ([example](https://github.com/better-auth/better-auth/issues/897), [another example](https://github.com/better-auth/better-auth/issues/6593)). The recommended workaround is to explicitly cast the user object to a type that includes your additional fields. For example: ```typescript import { auth } from "./your-auth-config-file"; emailVerification: { sendVerificationEmail: async ({ user, token }) => { const u = user as typeof auth.$Infer.Session.user; await sendBaseAppConfirmationEmail({ email: u.email, name: u.name, token, language: u.preferredLanguage, // No TS error now }); }, } ``` This tells TypeScript to treat user as your full inferred user type, including all additionalFields. You can also define your own interface if you prefer. Recent versions of Better Auth have improved type inference for additionalFields in some API responses and plugins, but as of 1.4.7, explicit casting is still required in hooks like sendVerificationEmail ([source](https://github.com/better-auth/better-auth/issues/6553), [source](https://github.com/better-auth/better-auth/issues/3888)). There’s ongoing work to make this more seamless in the future. Setting input: true or returned: true on your additionalFields can help with validation and API responses, but it does not affect the type inference in sendVerificationEmail. Let me know if you want a more detailed example or have questions about extending types elsewhere! <!-- 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/03f755ef-03fd-41a7-b8d5-3d25319b5f78?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/03f755ef-03fd-41a7-b8d5-3d25319b5f78?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/03f755ef-03fd-41a7-b8d5-3d25319b5f78?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/03f755ef-03fd-41a7-b8d5-3d25319b5f78?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/03f755ef-03fd-41a7-b8d5-3d25319b5f78?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/03f755ef-03fd-41a7-b8d5-3d25319b5f78?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/03f755ef-03fd-41a7-b8d5-3d25319b5f78?feedback_type=other)</sup> [![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Open in Cursor](https://dosu.dev/dosu-in-cursor.svg)](https://cursor.com/link/prompt?text=TypeScript%20complains%20about%20user.preferredLanguage%20in%20emailVerification%20because%20Better%20Auth%E2%80%99s%20type%20system%20doesn%E2%80%99t%20automatically%20merge%20your%20additionalFields%20into%20the%20User%20type%20used%20in%20sendVerificationEmail.%20This%20is%20a%20known%20limitation%3A%20the%20runtime%20user%20object%20includes%20your%20extra%20fields%2C%20but%20the%20type%20definition%20does%20not%2C%20so%20TypeScript%20flags%20them%20as%20missing%20even%20though%20they%E2%80%99re%20present%20at%20runtime%C2%A0%28%5Bexample%5D%28https%3A//github.com/better-auth/better-auth/issues/897%29%2C%20%5Banother%20example%5D%28https%3A//github.com/better-auth/better-auth/issues/6593%29%29.%0A%0AThe%20recommended%20workaround%20is%20to%20explicitly%20cast%20the%20user%20object%20to%20a%20type%20that%20includes%20your%20additional%20fields.%20For%20example%3A%0A%0A%60%60%60typescript%0Aimport%20%7B%20auth%20%7D%20from%20%22./your-auth-config-file%22%3B%0A%0AemailVerification%3A%20%7B%0A%20%20sendVerificationEmail%3A%20async%20%28%7B%20user%2C%20token%20%7D%29%20%3D%3E%20%7B%0A%20%20%20%20const%20u%20%3D%20user%20as%20typeof%20auth.%24Infer.Session.user%3B%0A%20%20%20%20await%20sendBaseAppConfirmationEmail%28%7B%0A%20%20%20%20%20%20email%3A%20u.email%2C%0A%20%20%20%20%20%20name%3A%20u.name%2C%0A%20%20%20%20%20%20token%2C%0A%20%20%20%20%20%20language%3A%20u.preferredLanguage%2C%20//%20No%20TS%20error%20now%0A%20%20%20%20%7D%29%3B%0A%20%20%7D%2C%0A%7D%0A%60%60%60%0A%0AThis%20tells%20TypeScript%20to%20treat%20user%20as%20your%20full%20inferred%20user%20type%2C%20including%20all%20additionalFields.%20You%20can%20also%20define%20your%20own%20interface%20if%20you%20prefer.%0A%0ARecent%20versions%20of%20Better%20Auth%20have%20improved%20type%20inference%20for%20additionalFields%20in%20some%20API%20responses%20and%20plugins%2C%20but%20as%20of%201.4.7%2C%20explicit%20casting%20is%20still%20required%20in%20hooks%20like%20sendVerificationEmail%C2%A0%28%5Bsource%5D%28https%3A//github.com/better-auth/better-auth/issues/6553%29%2C%20%5Bsource%5D%28https%3A//github.com/better-auth/better-auth/issues/3888%29%29.%20There%E2%80%99s%20ongoing%20work%20to%20make%20this%20more%20seamless%20in%20the%20future.%0A%0ASetting%20input%3A%20true%20or%20returned%3A%20true%20on%20your%20additionalFields%20can%20help%20with%20validation%20and%20API%20responses%2C%20but%20it%20does%20not%20affect%20the%20type%20inference%20in%20sendVerificationEmail.%0A%0ALet%20me%20know%20if%20you%20want%20a%20more%20detailed%20example%20or%20have%20questions%20about%20extending%20types%20elsewhere%21)&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/6886)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#19296