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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions