Uncaught TypeError: Cannot read property 'length' of undefined
Describe the bug When loading an etherpad, sometimes this error message pops up.
To Reproduce It happens randomly when starting etherpad. In our case, the etherpad is embedded into a different application The chance for this to happen seems to be higher, if you start many clients (open the pads) at the same time.
Expected behavior Well, the error message should not appear ;-)
Screenshots

Server (please complete the following information):
- Etherpad version: 1.8.13
- OS: Ubuntu 20.04.2
- Node.js version (
node --version): v14.16.1 - npm version (
npm --version): 6.14.12
Desktop (please complete the following information):
- OS: Window, Linux
- Browser: Chrome
- Version 91
Additional context
Uncaught TypeError: Cannot read property 'length' of undefined
at Object.toggleDropDown (VM6858 pad.js:37)
at Object.showModal (VM6858 pad.js:43)
at Object.disconnected (VM6858 pad.js:67)
at r.<anonymous> (VM6858 pad.js:3)
at r.emit (VM6857 socket.io.js:6)
at r.onevent (VM6857 socket.io.js:8)
at r.onpacket (VM6857 socket.io.js:8)
at r.<anonymous> (VM6857 socket.io.js:8)
at r.emit (VM6857 socket.io.js:6)
at r.ondecoded (VM6857 socket.io.js:6)
toggleDropDown @ pad.js?callback=require.define&v=d63febd6:37
showModal @ pad.js?callback=require.define&v=d63febd6:43
disconnected @ pad.js?callback=require.define&v=d63febd6:67
(anonymous) @ pad.js?callback=require.define&v=d63febd6:3
r.emit @ index.js:83
r.onevent @ index.js:83
r.onpacket @ index.js:83
(anonymous) @ index.js:83
r.emit @ index.js:83
r.ondecoded @ index.js:83
(anonymous) @ index.js:83
r.emit @ index.js:83
a.add @ index.js:83
r.ondata @ index.js:83
(anonymous) @ index.js:83
r.emit @ index.js:83
r.onPacket @ index.js:83
(anonymous) @ index.js:83
r.emit @ index.js:83
r.onPacket @ index.js:83
n @ index.js:83
e.decodePayload @ index.js:83
r.onData @ index.js:83
(anonymous) @ index.js:83
r.emit @ index.js:83
i.onData @ index.js:83
i.onLoad @ index.js:83
hasXDR.e.onreadystatechange @ index.js:83
XMLHttpRequest.send (async)
i.create @ index.js:83
i @ index.js:83
o.request @ index.js:83
o.doPoll @ index.js:83
r.poll @ index.js:83
r.onData @ index.js:83
(anonymous) @ index.js:83
r.emit @ index.js:83
i.onData @ index.js:83
i.onLoad @ index.js:83
hasXDR.e.onreadystatechange @ index.js:83
XMLHttpRequest.send (async)
i.create @ index.js:83
i @ index.js:83
o.request @ index.js:83
o.doPoll @ index.js:83
r.poll @ index.js:83
r.onData @ index.js:83
(anonymous) @ index.js:83
r.emit @ index.js:83
i.onData @ index.js:83
i.onLoad @ index.js:83
hasXDR.e.onreadystatechange @ index.js:83
XMLHttpRequest.send (async)
i.create @ index.js:83
i @ index.js:83
o.request @ index.js:83
o.doPoll @ index.js:83
r.poll @ index.js:83
r.doOpen @ index.js:83
r.open @ index.js:83
r.open @ index.js:83
r @ index.js:83
r @ index.js:83
r.open.r.connect @ index.js:83
r @ index.js:83
r @ index.js:83
r @ index.js:83
connect @ pad.js?callback=require.define&v=d63febd6:91
handshake @ pad.js?callback=require.define&v=d63febd6:3
(anonymous) @ pad.js?callback=require.define&v=d63febd6:3
l @ ace2_common.js?callback=require.define&v=d63febd6:38
c @ ace2_common.js?callback=require.define&v=d63febd6:38
setTimeout (async)
(anonymous) @ ace2_common.js?callback=require.define&v=d63febd6:38
u @ ace2_common.js?callback=require.define&v=d63febd6:38
add @ ace2_common.js?callback=require.define&v=d63febd6:38
(anonymous) @ ace2_common.js?callback=require.define&v=d63febd6:38
Deferred @ ace2_common.js?callback=require.define&v=d63febd6:38
then @ ace2_common.js?callback=require.define&v=d63febd6:38
m.fn.ready @ ace2_common.js?callback=require.define&v=d63febd6:38
init @ pad.js?callback=require.define&v=d63febd6:3
init @ pad.js?callback=require.define&v=d63febd6:3
(anonymous) @ Test_143?showChat=false&showLineNumbers=false&lang=de&userName=Peter+Tandler+teambits+(Orga-Team):749
setTimeout (async)
s @ ace2_common.js?callback=require.define&v=d63febd6:56
(anonymous) @ ace2_common.js?callback=require.define&v=d63febd6:56
u @ ace2_common.js?callback=require.define&v=d63febd6:38
fireWith @ ace2_common.js?callback=require.define&v=d63febd6:38
E @ ace2_common.js?callback=require.define&v=d63febd6:38
(anonymous) @ ace2_common.js?callback=require.define&v=d63febd6:38
load (async)
send @ ace2_common.js?callback=require.define&v=d63febd6:38
ajax @ ace2_common.js?callback=require.define&v=d63febd6:38
m.each.m.<computed> @ ace2_common.js?callback=require.define&v=d63febd6:38
getJSON @ ace2_common.js?callback=require.define&v=d63febd6:38
exports.update @ ace2_common.js?callback=require.define&v=d63febd6:56
(anonymous) @ Test_143?showChat=false&showLineNumbers=false&lang=de&userName=Peter+Tandler+teambits+(Orga-Team):741
(anonymous) @ Test_143?showChat=false&showLineNumbers=false&lang=de&userName=Peter+Tandler+teambits+(Orga-Team):759
Marking as bug, although I cannot 100% reproduce it. I saw it in the past during reconnects. I assume some code hits toggleDropDown during reconnect, but padeditbar.init was not called yet.
I found a race condition that I think is the culprit. I started working on a fix a few months ago, but the fix is much more complicated than I had originally thought so I put it on the back burner. See https://github.com/ether/etherpad-lite/compare/rhansen-collab_client for my work-in-progress commits.
@rhansen good to hear that you started working on a fix!
might be a stupid question and I haven't looked into any etherpad code yet: but what about adding a simple null check in toggleDropDown()?
As far as I understand, this part closes open drop downs. If t.dropdownsis undefined, it seems unlikely to me that there is anything to close, any popup-show classes to remove?