mongodb 7.5.0's collection.find({}).toArray() fails with TypeError: value is not a function. This is the next CRUD-fixture blocker after #11187 (find().sort()) and #11157. The failing call is CursorResponse.make(bson). make is a static method inherited from MongoDBResponse, and it uses module-scope bindings. When it is called through the subclass, those bindings read wrong values. The instrumented fixture showed typeof isErrorResponse === "object" when make ran as CursorResponse.make and "function" when it ran as MongoDBResponse.make.
It reproduces on origin/main 25ef463 without any package. It also reproduces with PR #11199 (#11187) and PR #11188 applied.
Package-free repro (two CommonJS files + entry)
rel/doc.js:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Doc = void 0;
class Doc {
constructor(bson, offset = 0, isArray = false, elements) { this.cache = Object.create(null); this.bson = bson; this.elements = elements ?? [bson.length]; }
get(name) { return this.cache[name] ?? null; }
}
exports.Doc = Doc;
rel/resp.js (reduced by greedy line removal from a mirror of mongodb/lib/cmap/wire_protocol/responses.js):
"use strict";
const lib_1 = { parse(b) { return [b.length]; } };
const doc_1 = require("./doc");
const Off = { a: 0, b: 1 };
function isErr(b, els) { for (let i = 0; i < els.length; i++) { if (els[i] === Off.b + 100) return true; } return b === "err"; }
class Base extends doc_1.Doc {
static make(bson) {
const elements = (0, lib_1.parse)(bson);
const isError = isErr(bson, elements);
return isError ? new Base(bson, 0, false, elements) : new this(bson, 0, false, elements);
}
}
exports.Base = Base;
class Sub extends Base {
constructor() { super(...arguments); this._batch = null; this.iterated = 0; }
get id() { try { return lib_1.parse(this.cursor); } catch (cause) { throw new err_1.E(cause.message); } }
}
exports.Sub = Sub;
class Sub2 extends Sub {
}
main.ts:
const r = require("./rel/resp.js");
for (const [l, f] of [["Base.make", () => r.Base.make("abc").constructor.name], ["Sub.make", () => r.Sub.make("abc").constructor.name]] as any[]) {
try { console.log(l, f()); } catch (e: any) { console.log(l, "THREW", e.message); }
}
Node 26.5.1:
Base.make Base
Sub.make Sub
Perry (main 25ef463, perry-dev, Linux x64, PERRY_NO_AUTO_OPTIMIZE=1):
Base.make Base
Sub.make THREW value is not a function
The reducer kept every one of these ingredients:
Sub's explicit constructor() { super(...arguments); ... }.
- A getter on
Sub.
- A further subclass
Sub2 extends Sub.
- A cross-module base,
Base extends doc_1.Doc.
- The module-scope bindings that
make reads: lib_1, isErr, and Off, which isErr reads.
Remove any one of them and the call succeeds. new Sub(...) called directly works, and so does Base.make. Only the inherited static that is invoked with this = Sub fails.
In mongodb
Fixture: mongodb 7.5.0 + bson 7.3.3, auto-optimize on, perry.compilePackages = mongodb and its deps. connect, deleteMany and insertMany succeed. Then await collection.find({}).toArray() fails, with or without .sort(). The throw originates in Connection.sendWire, at (responseType ?? MongoDBResponse).make(bson) with responseType = CursorResponse. Instrumentation inside MongoDBResponse.make printed:
make this MongoDBResponse ... typeof isErrorResponse = function (insert/delete: fine)
make this CursorResponse ... typeof isErrorResponse = object (find: throws calling it)
Possibly the same family as #10911 (a class's capturing static methods read captures as undefined). Here the captures come back as the wrong values, and only when the static is reached through a subclass.
mongodb 7.5.0's
collection.find({}).toArray()fails withTypeError: value is not a function. This is the next CRUD-fixture blocker after #11187 (find().sort()) and #11157. The failing call isCursorResponse.make(bson).makeis a static method inherited fromMongoDBResponse, and it uses module-scope bindings. When it is called through the subclass, those bindings read wrong values. The instrumented fixture showedtypeof isErrorResponse === "object"whenmakeran asCursorResponse.makeand"function"when it ran asMongoDBResponse.make.It reproduces on
origin/main25ef463 without any package. It also reproduces with PR #11199 (#11187) and PR #11188 applied.Package-free repro (two CommonJS files + entry)
rel/doc.js:rel/resp.js(reduced by greedy line removal from a mirror ofmongodb/lib/cmap/wire_protocol/responses.js):main.ts:Node 26.5.1:
Perry (main 25ef463, perry-dev, Linux x64,
PERRY_NO_AUTO_OPTIMIZE=1):The reducer kept every one of these ingredients:
Sub's explicitconstructor() { super(...arguments); ... }.Sub.Sub2 extends Sub.Base extends doc_1.Doc.makereads:lib_1,isErr, andOff, whichisErrreads.Remove any one of them and the call succeeds.
new Sub(...)called directly works, and so doesBase.make. Only the inherited static that is invoked withthis = Subfails.In mongodb
Fixture: mongodb 7.5.0 + bson 7.3.3, auto-optimize on,
perry.compilePackages= mongodb and its deps.connect,deleteManyandinsertManysucceed. Thenawait collection.find({}).toArray()fails, with or without.sort(). The throw originates inConnection.sendWire, at(responseType ?? MongoDBResponse).make(bson)withresponseType = CursorResponse. Instrumentation insideMongoDBResponse.makeprinted:Possibly the same family as #10911 (a class's capturing static methods read captures as undefined). Here the captures come back as the wrong values, and only when the static is reached through a subclass.