# Buggy code
def pageChanged(self, event):
    """Event called when the user swtiches between editor tabs."""
    old = event.GetOldSelection()
    # close any auto-complete or calltips when swtiching pages
    if old != wx.NOT_FOUND:
        oldPage = self.notebook.GetPage(old)
        if hasattr(oldPage, "CallTipActive"):
            if oldPage.CallTipActive():
                oldPage.CallTipCancel()
                oldPage.openBrackets = 0
        if hasattr(oldPage, "AutoCompActive"):
            if oldPage.AutoCompActive():
                oldPage.AutoCompCancel()

    new = event.GetSelection()
    self.currentDoc = self.notebook.GetPage(new)
    self.app.updateWindowMenu()
    self.setFileModified(self.currentDoc.UNSAVED)
    self.SetLabel("%s - PsychoPy Coder" % self.currentDoc.filename)

    self.currentDoc.analyseScript()

    fileType = self.currentDoc.getFileType()
    # enable run buttons if current file is a Python script
    if hasattr(self, "cdrBtnRunner"):
        isExp = fileType == "Python"
        self.toolbar.EnableTool(self.cdrBtnRunner.Id, isExp)
        self.toolbar.EnableTool(self.cdrBtnRun.Id, isExp)

    self.statusBar.SetStatusText(fileType, 2)

    # todo: reduce redundancy w.r.t OnIdle()
    if not self.expectedModTime(self.currentDoc):
        filename = os.path.basename(self.currentDoc.filename)
        msg = (
            _translate(
                "'%s' was modified outside of PsychoPy:\n\nReload (without saving)?"
            )
            % filename
        )
        dlg = dialogs.MessageDialog(self, message=msg, type="Warning")
        if dlg.ShowModal() == wx.ID_YES:
            self.statusBar.SetStatusText(_translate("Reloading file"))
            self.fileReload(event, filename=self.currentDoc.filename, checkSave=False)
            self.setFileModified(False)
        self.statusBar.SetStatusText("")
        dlg.Destroy()


# Traceback (Exception)
Traceback (most recent call last):
File "C:\Program Files\PsychoPy3\lib\site-packages\psychopy\app\coder\coder.py", line 2004, in pageChanged
oldPage = self.notebook.GetPage(old)
File "C:\Program Files\PsychoPy3\lib\site-packages\wx\lib\agw\aui\auibook.py", line 4440, in GetPage
raise Exception("invalid notebook page")
Exception: invalid notebook page

# Fixed code
def pageChanged(self, event):
    """Event called when the user switches between editor tabs."""
    old = event.GetOldSelection()
    # close any auto-complete or calltips when switching pages
    if old != wx.NOT_FOUND:
        oldPage = None

        try:  # last page was closed, this will raise and error
            oldPage = self.notebook.GetPage(old)
        except Exception:
            pass

        if oldPage is not None:
            if hasattr(oldPage, "CallTipActive"):
                if oldPage.CallTipActive():
                    oldPage.CallTipCancel()
                    oldPage.openBrackets = 0
            if hasattr(oldPage, "AutoCompActive"):
                if oldPage.AutoCompActive():
                    oldPage.AutoCompCancel()

    new = event.GetSelection()
    self.currentDoc = self.notebook.GetPage(new)
    self.app.updateWindowMenu()
    self.setFileModified(self.currentDoc.UNSAVED)
    self.SetLabel("%s - PsychoPy Coder" % self.currentDoc.filename)

    self.currentDoc.analyseScript()

    fileType = self.currentDoc.getFileType()
    # enable run buttons if current file is a Python script
    if hasattr(self, "cdrBtnRunner"):
        isExp = fileType == "Python"
        self.toolbar.EnableTool(self.cdrBtnRunner.Id, isExp)
        self.toolbar.EnableTool(self.cdrBtnRun.Id, isExp)

    self.statusBar.SetStatusText(fileType, 2)

    # todo: reduce redundancy w.r.t OnIdle()
    if not self.expectedModTime(self.currentDoc):
        filename = os.path.basename(self.currentDoc.filename)
        msg = (
            _translate(
                "'%s' was modified outside of PsychoPy:\n\nReload (without saving)?"
            )
            % filename
        )
        dlg = dialogs.MessageDialog(self, message=msg, type="Warning")
        if dlg.ShowModal() == wx.ID_YES:
            self.statusBar.SetStatusText(_translate("Reloading file"))
            self.fileReload(event, filename=self.currentDoc.filename, checkSave=False)
            self.setFileModified(False)
        self.statusBar.SetStatusText("")
        dlg.Destroy()