Skip to content

auth.createUser({ displayName: null }) type-checks but throws auth/invalid-display-name at runtime #3152

@llamington

Description

@llamington

[READ] Step 1: Are you in the right place?

Yes — bug in the auth component of this repo (typing + runtime divergence between createUser and updateUser).

[REQUIRED] Step 2: Describe your environment

  • Operating System version: macOS 15.2 (also reproduces on Linux per type definitions alone)
  • Firebase SDK version: firebase-admin@13.6.0
  • Firebase Product: auth
  • Node.js version: 22.x
  • NPM version: 10.x

[REQUIRED] Step 3: Describe the problem

CreateRequest extends UpdateRequest, so displayName, photoURL, and phoneNumber are typed string | null | undefined on the create path. The runtime, however, only handles null on the update path — updateExistingAccount rewrites displayName: null into deleteAttribute: ['DISPLAY_NAME'] before validation (src/auth/auth-api-request.ts, the deletableParams block). createNewAccount has no such transform, so null flows straight into validateCreateEditRequest, where:

if (typeof request.displayName !== 'undefined' &&
    !validator.isString(request.displayName)) {
    throw new FirebaseAuthError(AuthClientErrorCode.INVALID_DISPLAY_NAME);
}

isString is typeof v === 'string', so null fails. The type permits a value the runtime always rejects.

The same shape applies to photoURL and phoneNumber on CreateRequest.

Steps to reproduce:

  1. Compile the snippet below — TypeScript accepts it.
  2. Run against a real Firebase Auth project (or the emulator).
  3. Observe FirebaseAuthError { code: 'auth/invalid-display-name' }.

Relevant Code:

import { getAuth } from 'firebase-admin/auth'

await getAuth().createUser({
  uid: 'repro-' + Date.now(),
  displayName: null, // typed as string | null, but runtime rejects null
})
// FirebaseAuthError: The displayName field must be a valid string.
//   code: 'auth/invalid-display-name'

// Contrast — this works because updateUser rewrites null into deleteAttribute:
await getAuth().updateUser('some-existing-uid', { displayName: null })

Expected

Either:

  • Tighten the type: override the inherited members on CreateRequest to drop | null — i.e. displayName?: string, photoURL?: string, phoneNumber?: string — so the compiler rejects calls the runtime would reject; or
  • Relax the runtime: in createNewAccount, strip null from these fields before validation (on the create path, null has no "clear" semantics — there is no existing value to clear, so null is equivalent to omitted).

Happy to send a PR for whichever direction you prefer.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions