---
title: "Fix 'transport.sendMail is not a function' in Payload CMS"
slug: "fix-transport-sendmail-not-a-function-payload-nodemailer"
published: "2026-01-12"
updated: "2026-02-22"
categories:
  - "Payload"
tags:
  - "transport.sendMail is not a function"
  - "Payload CMS email"
  - "@payloadcms/email-nodemailer"
  - "nodemailer v7"
  - "nodemailer v6"
  - "downgrade nodemailer"
  - "sendMail undefined error"
  - "pnpm remove nodemailer"
  - "payload.config.ts email"
  - "SMTP transport troubleshooting"
llm-intent: "how-to"
audience-level: "intermediate"
llm-purpose: "transport.sendMail is not a function in Payload CMS? Fix it by downgrading Nodemailer to v6 with step-by-step pnpm commands and quick verification. Read…"
llm-prereqs:
  - "Payload CMS"
  - "nodemailer"
  - "@payloadcms/email-nodemailer"
  - "Node.js"
  - "pnpm"
  - "TypeScript"
  - "next-auth"
---

**Summary Triples**
- (Fix 'transport.sendMail is not a function' in Payload CMS, expresses-intent, how-to)
- (Fix 'transport.sendMail is not a function' in Payload CMS, covers-topic, transport.sendMail is not a function)
- (Fix 'transport.sendMail is not a function' in Payload CMS, provides-guidance-for, transport.sendMail is not a function in Payload CMS? Fix it by downgrading Nodemailer to v6 with step-by-step pnpm commands and quick verification. Read…)

### {GOAL}
transport.sendMail is not a function in Payload CMS? Fix it by downgrading Nodemailer to v6 with step-by-step pnpm commands and quick verification. Read…

### {PREREQS}
- Payload CMS
- nodemailer
- @payloadcms/email-nodemailer
- Node.js
- pnpm
- TypeScript
- next-auth

### {STEPS}
1. Reproduce and confirm the error
2. Remove Nodemailer v7 from project
3. Install Nodemailer v6 and types
4. Verify payload.config.ts transport setup
5. Restart dev server and test flows

<!-- llm:goal="transport.sendMail is not a function in Payload CMS? Fix it by downgrading Nodemailer to v6 with step-by-step pnpm commands and quick verification. Read…" -->
<!-- llm:prereq="Payload CMS" -->
<!-- llm:prereq="nodemailer" -->
<!-- llm:prereq="@payloadcms/email-nodemailer" -->
<!-- llm:prereq="Node.js" -->
<!-- llm:prereq="pnpm" -->
<!-- llm:prereq="TypeScript" -->
<!-- llm:prereq="next-auth" -->

# Fix 'transport.sendMail is not a function' in Payload CMS
> transport.sendMail is not a function in Payload CMS? Fix it by downgrading Nodemailer to v6 with step-by-step pnpm commands and quick verification. Read…
Matija Žiberna · 2026-01-12

I was setting up email functionality for a Payload CMS v3 project when I hit a confusing error. The configuration looked perfect, the SMTP credentials were correct, but every time the forgot password flow triggered, I got this cryptic error:

```
TypeError: transport.sendMail is not a function
```

The frustrating part? The configuration was copied straight from the official Payload documentation. After digging through node_modules and testing different approaches, I discovered the root cause: a version incompatibility between `nodemailer` v7 and the `@payloadcms/email-nodemailer` adapter. This guide shows you exactly how to fix it.

## The Problem: When Documentation Isn't Enough

Here's what my `payload.config.ts` looked like initially:

```typescript
// File: payload.config.ts
import { nodemailerAdapter } from '@payloadcms/email-nodemailer'
import nodemailer from 'nodemailer'

export default buildConfig({
  // ... other config
  email: nodemailerAdapter({
    defaultFromAddress: process.env.SMTP_FROM_ADDRESS || 'info@example.com',
    defaultFromName: 'My App',
    transport: nodemailer.createTransport({
      host: process.env.SMTP_SERVER || 'smtp.gmail.com',
      port: 587,
      secure: false,
      auth: {
        user: process.env.SMTP_LOGIN,
        pass: process.env.SMTP_PASSWORD,
      },
    }),
  }),
})
```

This configuration is textbook correct. The `createTransport` call returns a transport object, which should have a `sendMail` method. But when Payload tried to send an email, it crashed.

The error stack trace pointed to the adapter's internal code trying to call `transport.sendMail`, which apparently didn't exist. This didn't make sense because `nodemailer.createTransport()` always returns an object with `sendMail`.

## Investigating the Root Cause

I created a simple reproduction script to test if the issue was with my Nodemailer installation:

```typescript
// File: reproduce_issue.ts
import nodemailer from 'nodemailer';

const transport = nodemailer.createTransport({
  host: 'smtp.gmail.com',
  port: 587,
  auth: { user: 'test', pass: 'test' },
});

console.log('Transport has sendMail:', typeof transport.sendMail === 'function');

const spreadTransport = { ...transport };
console.log('Spread transport has sendMail:', typeof spreadTransport.sendMail === 'function');
```

Running this revealed something critical:

```
Nodemailer version: 7.0.12
Transport has sendMail: true
Spread transport has sendMail: false
HYPOTHESIS CONFIRMED: Spread operator kills sendMail.
```

The transport object has `sendMail` as a prototype method, not an own property. When you spread the object with `{ ...transport }`, you only copy enumerable own properties. The prototype methods like `sendMail` are lost.

This meant that somewhere in the Payload adapter's code, the transport object was being spread or restructured in a way that broke the method chain. Checking the adapter's package.json confirmed it:

```json
{
  "name": "@payloadcms/email-nodemailer",
  "version": "3.71.1",
  "dependencies": {
    "nodemailer": "7.0.12"
  }
}
```

The adapter ships with `nodemailer` v7 as a dependency, but the internal implementation doesn't handle v7's object structure correctly.

## The Solution: Downgrade to Nodemailer v6

The fix is straightforward: downgrade `nodemailer` to v6, which the adapter was originally designed to work with. Here's the step-by-step process.

### Step 1: Remove Nodemailer v7

First, remove the existing `nodemailer` installation:

```bash
pnpm remove nodemailer @types/nodemailer
```

### Step 2: Install Nodemailer v6

Install the v6 version explicitly:

```bash
pnpm add nodemailer@^6.9.16
pnpm add -D @types/nodemailer@^6.4.17
```

You'll see a peer dependency warning from `next-auth` if you're using it, since it expects v7. You can safely ignore this warning. The Payload adapter issue takes priority, and `next-auth` will work fine with v6.

### Step 3: Verify Your Configuration

Your `payload.config.ts` should use the standard `createTransport` approach:

```typescript
// File: payload.config.ts
import { nodemailerAdapter } from '@payloadcms/email-nodemailer'
import nodemailer from 'nodemailer'

export default buildConfig({
  email: nodemailerAdapter({
    defaultFromAddress: process.env.SMTP_FROM_ADDRESS || 'info@example.com',
    defaultFromName: 'My App',
    skipVerify: false,
    transport: nodemailer.createTransport({
      host: process.env.SMTP_SERVER || 'smtp.gmail.com',
      port: 587,
      secure: false, // Use STARTTLS on port 587
      auth: {
        user: process.env.SMTP_LOGIN,
        pass: process.env.SMTP_PASSWORD,
      },
    }),
  }),
})
```

The key here is passing the `transport` property with a fully constructed transport object. Don't use `transportOptions` as a workaround, even though the adapter supports it. With v6, the direct transport approach is more reliable.

### Step 4: Restart Your Dev Server

Kill your development server and restart it to ensure the new version is loaded:

```bash
# Stop the server (Ctrl+C)
pnpm run dev
```

Now test the forgot password flow or any other email-sending functionality. The `transport.sendMail is not a function` error should be gone.

## Why This Happens

The incompatibility stems from how Nodemailer v7 restructured its internal object model. In v6, the transport object's methods were more directly accessible, even after certain object operations. In v7, the methods are strictly prototype-based, which means any code that spreads or reconstructs the object will lose them.

The `@payloadcms/email-nodemailer` adapter was built and tested against v6. When v7 was released, the adapter's dependency was updated, but the internal implementation wasn't adjusted to handle the new structure. This is why the error is so confusing: the configuration is correct, the credentials work, but the runtime fails.

## Conclusion

The `transport.sendMail is not a function` error in Payload CMS is caused by a version mismatch between `nodemailer` v7 and the `@payloadcms/email-nodemailer` adapter. By downgrading to `nodemailer` v6, you restore compatibility and get email functionality working as expected.

This is a perfect example of why pinning dependencies matters. Even when a library follows semantic versioning, internal changes can break integrations in non-obvious ways. If you're setting up Payload CMS email and hit this error, downgrade to v6 and you'll be back on track.

Let me know in the comments if you have questions, and subscribe for more practical development guides.

Thanks,  
Matija