{"record":{"id":"278c16ddfd310b76","repo":"Automattic/mongoose","slug":"invalid-graphlookup-argument-must-be-an-object","errorCode":null,"errorMessage":"Invalid graphLookup() argument. Must be an object.","messagePattern":"Invalid graphLookup\\(\\) argument\\. Must be an object\\.","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"warning","filePath":"lib/aggregate.js","lineNumber":595,"sourceCode":" *\n * Note that graphLookup can only consume at most 100MB of memory, and does not allow disk use even if `{ allowDiskUse: true }` is specified.\n *\n * #### Example:\n *\n *      // Suppose we have a collection of courses, where a document might look like `{ _id: 0, name: 'Calculus', prerequisite: 'Trigonometry'}` and `{ _id: 0, name: 'Trigonometry', prerequisite: 'Algebra' }`\n *      aggregate.graphLookup({ from: 'courses', startWith: '$prerequisite', connectFromField: 'prerequisite', connectToField: 'name', as: 'prerequisites', maxDepth: 3 }) // this will recursively search the 'courses' collection up to 3 prerequisites\n *\n * @see $graphLookup https://www.mongodb.com/docs/manual/reference/operator/aggregation/graphLookup/#pipe._S_graphLookup\n * @param {object} options to $graphLookup as described in the above link\n * @return {Aggregate}\n * @api public\n */\n\nAggregate.prototype.graphLookup = function(options) {\n  const cloneOptions = {};\n  if (options) {\n    if (!utils.isObject(options)) {\n      throw new TypeError('Invalid graphLookup() argument. Must be an object.');\n    }\n\n    utils.mergeClone(cloneOptions, options);\n    const startWith = cloneOptions.startWith;\n\n    if (startWith && typeof startWith === 'string') {\n      cloneOptions.startWith = cloneOptions.startWith.startsWith('$') ?\n        cloneOptions.startWith :\n        '$' + cloneOptions.startWith;\n    }\n\n  }\n  return this.append({ $graphLookup: cloneOptions });\n};\n\n/**\n * Appends new custom $sample operator to this aggregate pipeline.\n *","sourceCodeStart":577,"sourceCodeEnd":613,"githubUrl":"https://github.com/Automattic/mongoose/blob/49cdab01366679723b487ecb754b38570f783289/lib/aggregate.js#L577-L613","documentation":"Mongoose prints this warning at require time when it detects Jest fake timers: with mocked timers, setTimeout has a .clock object with a Date function (jest.useFakeTimers() installs it). Mongoose and the MongoDB driver rely on real timers for connection timeouts, heartbeats, and immediate callbacks, so fake timers can hang or slow every DB operation in a test. The check is typeof setTimeout.clock?.Date === 'function'; SUPPRESS_JEST_WARNINGS hides it.","triggerScenarios":"Calling jest.useFakeTimers() (legacy or modern) before or after requiring mongoose in the same test file; global fake-timer setup files in jest.setup.js applied to every test; waitFor/advanceTimersByTime loops around code that opens a MongoDB connection.","commonSituations":"Unit tests that fake timers for debouncing/scheduling code in the same file as model tests; setupFilesAfterEach enabling fake timers globally; CI flakiness where connections time out because timer callbacks never fire; migration to modern fake timers which also set setTimeout.clock.","solutions":["Scope fake timers to the tests that need them and restore afterwards: afterEach(() => jest.useRealTimers()).","Move DB-dependent tests into files that never enable fake timers (often paired with testEnvironment: 'node').","If you must keep fake timers, configure them to leave some real timers: jest.useFakeTimers({ doNotFake: ['nextTick', 'setImmediate', 'setTimeout'] }) as appropriate.","Use jest.useFakeTimers({ advanceTimers: false }) plus explicit jest.advanceTimersByTime() only around the scheduling code under test, not the connection logic."],"exampleFix":"// before\njest.useFakeTimers();\nrequire('mongoose'); // warning + hanging queries\n\n// after\nbeforeEach(() => jest.useFakeTimers());\nafterEach(() => jest.useRealTimers());\n// DB tests live in files that never fake timers","handlingStrategy":"validation","validationCode":"// jest.setup.js — keep real timers for anything touching the DB\nbeforeEach(() => {\n  jest.useRealTimers();\n});","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pair jest.useFakeTimers() with afterEach(() => jest.useRealTimers()).","Never enable fake timers in global setup files shared by DB tests.","If scheduling code must be tested with fake timers, isolate it in its own test file from connection code."],"tags":["mongoose","jest","fake-timers","testing","warning"],"backgroundTag":"jest-fake-timers","analyzedSha":"49cdab01366679723b487ecb754b38570f783289","analyzedAt":"2026-08-21T22:54:00.882Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}