Prebid.js: Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'makeBidRequests')

Type of issue

Bug

Description

We wanted to update our version of Prebid.js from 6.12.0 to the latest version 6.22.0. Once updated, we noticed this error in the console

Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'makeBidRequests')

IsawNCGP

After several tests, it seems that we have the error when switching to Prebid.js starting 6.17.0 and higher. The possibilities we see in the diffs may be related to the adapterManager.js file via Prebid core & PBS adapter: better support for server-side stored impressions by dgirardi · Pull Request #8154 · prebid/Prebid.js ?

or to the webpack upgrade from 3.0.0 to 5.70.0 via Build system: upgrade webpack by dgirardi · Pull Request #7935 · prebid/Prebid.js ?

we have tried some fixes, here we import adapterManager.js via a require

Prebid.js/auction.js at e07025247a81bf8c04501de8c66d817143cc30d1 · prebid/Prebid.js

we added some logs to understand, and replaced require with an import

adapterManager => import adapterManager from './adapterManager.js';

_adapterManager => const _adapterManager = require('./adapterManager.js').default;

We see that on the second call of callBids, adapterManager becomes undefined when imported with a require while it is not the case when imported with an import.

But we don’t know if this is the real cause of the error.

image

Steps to reproduce

Test page

https://7buoei.vercel.app/test.html

Expected results

No errors in console

Actual results

Error in console

Platform details

dependency/environment version
Prebid.js 6.22.0
Prebid.js global ‘paubjs’
npm 8.5.5
node 16.15.0
browser Chrome/100.0.4896.75
OS macOS 10.15.7

Feel free if you need more details.

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 33 (11 by maintainers)

Commits related to this issue

Most upvoted comments

Discussed this today; the plan is to ship a change in Prebid 7 that would prevent the second copy of prebid from running if it’s using the same globalVarName as an instance of Prebid that’s already running.

If you’re using an earlier version of Prebid, and you’re affected by this issue, you should prevent the second loading of Prebid yourself. Note that even in 6.12 and earlier, the Prebid codebase assumes one instance per globalVarName - just because it doesn’t show obvious exceptions doesn’t mean it works if you load it multiple times; we never tested that scenario.

I did some more digging, and I think the proper fix is to prevent Prebid from loading twice - I would not trust the switch to import to solve this. (Paging @coreyb-cbs as well).

The issue with the proper fix is that it might be a breaking change if we do it within Prebid. I’ll plan to get it in for 7 at least.

What happens is:

  • webpack maintains a registry of all modules (where modules are roughly javascript files). It starts out empty.
  • the first time Prebid is loaded, webpack fills out the registry recursively - e.g. when it’s time to run auction.js, it creates an object for it, then executes the code so that it can be filled with the exported symbols. When auction.js does require('./adapterManager.js'), the same happens - and so on along the dependency graph, as you’d expect.
  • the second time Prebid is loaded, because the registry already contains entries for adapterManager etc, code runs in a different order - it does not follow dependencies. In particular, because of a circular import, it runs auction.js before the bulk of adapterManager.js. The object returned by require('./adapterManager.js') is incomplete.
  • the reason import seems to fix it is that it gets compiled in a slightly different way. Instead of:
    // with require
    var adapterManager = (__webpack_require__("./src/adapterManager.js")["default"]);
    // ....
    function callBids() {
        var bidRequests = adapterManager.makeBidRequests(_adUnits, _auctionStart, _auctionId, _timeout, _labels);  
    
    it does:
    // with import
    var _adapterManager_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__("./src/adapterManager.js");
    function callBids() {
       var bidRequests = _adapterManager_js__WEBPACK_IMPORTED_MODULE_4__["default"].makeBidRequests(_adUnits, 
    _auctionStart, _auctionId, _timeout, _labels);
    }
    

That is, import happens to generate a lazy dereference in this case. But not in general; these idioms would have the same problem:

import * as am from './src/adapterManager.js';
const adapterManager = am['default'];
function callBids() {
   adapterManager.makeBidRequests(...)
}

// or when there's no option to be lazy

import {someSymbol} from './someModule';
someSymbol();

The bottom line is that even if everything happens to work right now after switching to import, it could easily break (for the second load of Prebid) just by shuffling around their order, introduction of new circular imports, or refactoring of imports to forms that will appear equivalent.

If you’re using Webpack, can you post its version and the configuration you’re using for it?

I encountered the exact same issue yesterday while attempting to upgrade from Prebid 6.8 to 6.22 and, like you, I also noticed that the issue is resolved if the require is changed to an import in auction.js.

  • @dgirardi this is what we discussed via Slack yesterday