    def write_error(self, status_code, **kwargs):
        """render custom error pages"""
        exc_info = kwargs.get('exc_info')
        message = ''
        exception = None
        status_message = responses.get(status_code, 'Unknown HTTP Error')
        if exc_info:
            exception = exc_info[1]
            # get the custom message, if defined
            try:
                message = exception.log_message % exception.args
            except Exception:
                pass

            # construct the custom reason, if defined
            reason = getattr(exception, 'reason', '')
            if reason:
                message = reasons.get(reason, reason)

        if exception and isinstance(exception, SQLAlchemyError):
            self.log.warning("Rolling back session due to database error %s", exception)
            self.db.rollback()

        # build template namespace
        ns = dict(
            status_code=status_code,
            status_message=status_message,
            message=message,
            extra_error_html=getattr(self, 'extra_error_html', ''),
            exception=exception,
        )

        self.set_header('Content-Type', 'text/html')
        if isinstance(exception, web.HTTPError):
            # allow setting headers from exceptions
            # since exception handler clears headers
            headers = getattr(exception, 'headers', None)
            if headers:
                for key, value in headers.items():
                    self.set_header(key, value)
            # Content-Length must be recalculated.
            self.clear_header('Content-Length')

        # render_template is async, but write_error can't be!
        # so we run it sync here, instead of making a sync version of render_template

        try:
            html = self.render_template(f'{status_code}.html', sync=True, **ns)
        except TemplateNotFound:
            self.log.debug("Using default error template for %d", status_code)
            try:
                html = self.render_template('error.html', sync=True, **ns)
            except Exception:
                # In this case, any side effect must be avoided.
                ns['no_spawner_check'] = True
                html = self.render_template('error.html', sync=True, **ns)

        self.write(html)
render custom error pages
jupyterhub/handlers/base.py