mongodb/node-mongodb-native · error · MongoAPIError

Cursor returned a `null` document, but the cursor is not exh

Error message

Cursor returned a `null` document, but the cursor is not exhausted.  Mapping documents to `null` is not supported in the cursor transform.

What it means

Thrown by AbstractCursor.transformDocument() when the user-supplied transform function (set via cursor.map()) returns null for a document. The driver cannot distinguish a null-transformed result from the sentinel null it uses to signal cursor exhaustion in its iteration protocol, so it refuses the mapping entirely. This is a user-API contract error, not a server problem.

Source

Thrown at src/cursor/abstract_cursor.ts:1076

        // @ts-expect-error: CursorEvents is generic so Parameters<CursorEvents["close"]> may not be assignable to `[]`. Not sure how to require extenders do not add parameters.
        this.emit('close');
      }
    } finally {
      this.hasEmittedClose = true;
    }
  }

  /** @internal */
  private async transformDocument(document: NonNullable<TSchema>): Promise<NonNullable<TSchema>> {
    if (this.transform == null) return document;

    try {
      const transformedDocument = this.transform(document);
      // eslint-disable-next-line no-restricted-syntax
      if (transformedDocument === null) {
        const TRANSFORM_TO_NULL_ERROR =
          'Cursor returned a `null` document, but the cursor is not exhausted.  Mapping documents to `null` is not supported in the cursor transform.';
        throw new MongoAPIError(TRANSFORM_TO_NULL_ERROR);
      }
      return transformedDocument;
    } catch (transformError) {
      try {
        await this.close();
      } catch (closeError) {
        squashError(closeError);
      }
      throw transformError;
    }
  }

  /** @internal */
  protected throwIfInitialized() {
    if (this.initialized) throw new MongoCursorInUseError();
  }
}

View on GitHub (pinned to 3366c21a63)

Solutions

  1. Do not return null from map(); return a placeholder object (e.g. { __skip: true }) and filter afterwards, or return undefined and filter it out
  2. Use cursor.filter() / a $match stage to drop documents server-side instead of map()
  3. Apply the transform yourself after toArray() rather than via cursor.map()

Example fix

// before
cursor.map(doc => doc.active ? doc : null); // throws
// after (filter server-side)
const docs = await coll.find({ active: true }).toArray();
// or map to a sentinel and post-filter
cursor.map(doc => doc.active ? doc : undefined);
const docs = (await cursor.toArray()).filter(Boolean);
Defensive patterns

Strategy: validation

Validate before calling

function makeTransform(fn) {
  return (doc) => {
    const out = fn(doc);
    if (out === null) {
      throw new Error('transform returned null; use filter() instead of map() for dropping docs');
    }
    return out;
  };
}
// usage: cursor.map(makeTransform(d => d.active ? d : null)) // fails fast, clearly

Type guard

const returnsNonNull = (fn) => (doc) => {
  const r = fn(doc);
  if (r == null) throw new Error('transform must not return null/undefined');
  return r;
};

Try / catch

try {
  await cursor.toArray();
} catch (e) {
  if (e instanceof MongoAPIError && /Mapping documents to `null`/.test(e.message)) {
    // rewrite to filter server-side
    docs = await coll.find(filter).toArray();
  } else throw e;
}

Prevention

When it happens

Trigger: Calling cursor.map(doc => conditional ? null : doc), or a transform that legitimately returns null (e.g. filtering via map, or optional fields mapped to null). Triggered on the first document for which the transform yields null during next()/toArray()/forEach()/streaming.

Common situations: Using map() to drop documents, projecting a field that can be null, or migrating a filter/map chain from another library that permits null.

Related errors


AI-assisted analysis of mongodb/node-mongodb-native@3366c21a63 (2026-08-04). Data as JSON: /data/errors/e2ab728239dc2e46.json. Report an issue: GitHub.