Internal Server Error with new installed PLONE 5.2.1

I installed a new downloaded PLONE 5.2.1 with unifed Installer Plone-5.2.1-UnifiedInstaller-r3 with ZEO option. After having adjusted the Ports in buildout.cfg and running buildout I get this message, when calling the clients od ZMI:

Internal Server Error
The server encountered an unexpected internal server error
(generated by waitress)

When running in fg I see
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 4: ordinal not in range(128)

Can't find a solution in the other posts regarding this.

full traceback please

Here it is:

Serving on http://0.0.0.0:8083
2020-03-16 11:57:03,981 ERROR   [waitress:357][waitress] Exception while serving /
Traceback (most recent call last):
  File "/home/users/mlpd/Plone/buildout-cache/eggs/waitress-1.4.3-py2.7.egg/waitress/channel.py", line 349, in service
    task.service()
  File "/home/users/mlpd/Plone/buildout-cache/eggs/waitress-1.4.3-py2.7.egg/waitress/task.py", line 169, in service
    self.execute()
  File "/home/users/mlpd/Plone/buildout-cache/eggs/waitress-1.4.3-py2.7.egg/waitress/task.py", line 439, in execute
    app_iter = self.channel.server.application(environ, start_response)
  File "/home/users/mlpd/Plone/buildout-cache/eggs/Paste-3.1.1-py2.7.egg/paste/translogger.py", line 69, in __call__
    return self.application(environ, replacement_start_response)
  File "/home/users/mlpd/Plone/buildout-cache/eggs/Zope-4.1.3-py2.7.egg/ZPublisher/httpexceptions.py", line 30, in __call__
    return self.application(environ, start_response)
  File "/home/users/mlpd/Plone/buildout-cache/eggs/Zope-4.1.3-py2.7.egg/ZPublisher/WSGIPublisher.py", line 357, in publish_module
    start_response(status, headers)
  File "/home/users/mlpd/Plone/buildout-cache/eggs/Paste-3.1.1-py2.7.egg/paste/translogger.py", line 67, in replacement_start_response
    self.write_log(environ, method, req_uri, start, status, bytes)
  File "/home/users/mlpd/Plone/buildout-cache/eggs/Paste-3.1.1-py2.7.egg/paste/translogger.py", line 99, in write_log
    message = self.format % d
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 4: ordinal not in range(128)

Check for the offending data with the Python debugger in:

Any reasons why you need Python 2.7 instead Python 3.X?

I understand, python comes with unified Installer. I did not change anything so that the python Version, that was installed, is used.

No idea what the universal installer is doing.
Use Python 3 if you can with 5.2 unless there are reasons to do so,

Unified Installer (not "universal installer") is the official install package of PLONE as it is available on plone.org. I would expect, that all components work correctly togather, when it is installed with no modifications.
Is anybody there who is in packageing of PLONE to help?

As said: the first thing is to figure out the offending data using the Python debugger. Then please file a bug report. Exceptations and reality often do not match for a variety of reasons.

Line 99 of translogger.py is:
message = self.format % d
Have set the debug break point a line in front of it.

The content of variable d :

/home/users/mlpd/Plone/buildout-cache/eggs/Paste-3.1.1-py2.7.egg/paste/translogger.py(100)write_log()
-> message = self.format % d
(Pdb) print d
{'REQUEST_METHOD': 'GET', 'status': '200', 'REMOTE_USER': u'admin', 'HTTP_USER_AGENT': 'Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0', 'HTTP_REFERER': '-', 'REMOTE_ADDR': '93.206.36.15', 'HTTP_VERSION': 'HTTP/1.1', 'time': '17/M\xc3\xa4r/2020:09:49:53 +0200', 'bytes': '7807', 'REQUEST_URI': '/'}
(Pdb)

The error prone Value 0xc3 which seems to be in the 'time' position and is not in the ASCII range would be in unicode à (?)

and self.format?

(Pdb) print self.format
%(REMOTE_ADDR)s - %(REMOTE_USER)s [%(time)s] "%(REQUEST_METHOD)s %(REQUEST_URI)s %(HTTP_VERSION)s" %(status)s %(bytes)s "%(HTTP_REFERER)s" "%(HTTP_USER_AGENT)s"

There is some unexpected German month name inside the data

'time': '17/M\xc3\xa4r/2020:09:49:53 +0200

The month name should clearly be in English.

Looks as the locales are set to German and the logger is picking the month name from the locales
(which it should not)...something like that..

Check our environment using env|grep LC...likely there will be something like

LC_ALL=en_US.UTF-8
LC_MEASUREMENT=en_US.UTF-8
LC_PAPER=en_US.UTF-8
LC_MONETARY=en_US.UTF-8
LC_NAME=en_US.UTF-8
LC_CTYPE=en_US.UTF-8
LC_ADDRESS=en_US.UTF-8
LC_NUMERIC=en_US.UTF-8
LC_TELEPHONE=en_US.UTF-8
LC_IDENTIFICATION=en_US.UTF-8
LC_TIME=en_US.UTF-8

but with de_DE or so

Yesterday, i have the same error on python2 with coredevbuild and the installer. i need it for an upgrade. my solution:

vim ~/.bashrc
# override my language settings
export LC_ALL=en_US.UTF-8
export LANG=en_US.UTF-8
export LANGUAGE=en_US.UTF-8
source ~/.bashrc
1 Like

Thanks for spending your time on that.
But In meantime I have successfully installed ZEO with PLONE 5.0.10 and succesfully migrated our most used website from PLONE 5.0.7 Standalone.
BTW: 5.0.10 was the only PLONE 5.0.x Version that was installable with Unified Installer with ZEO Option.and worked without Problems.

I will create a bug report on https://github.com/plone/Products.CMFPlone/issues.

Which version of 'Paste' is used in the 5.0.x ?

As said: setting your locale environment variables correctly should resolve the problem.

German is correctly :wink:

How is this relevant with Plone?

We encountered the exact same issue on Plone 5.2.11 on Python 2.7.18 and figured out the following:

The issue happens in Paste-3.5.2-py2.7.egg/paste/translogger.py at line 99:

    def write_log(self, environ, method, req_uri, start, status, bytes):
        if bytes is None:
            bytes = '-'
        if time.daylight:
                offset = time.altzone / 60 / 60 * -100
        else:
                offset = time.timezone / 60 / 60 * -100
        if offset >= 0:
                offset = "+%0.4d" % (offset)
        elif offset < 0:
                offset = "%0.4d" % (offset)
        remote_addr = '-'
        if environ.get('HTTP_X_FORWARDED_FOR'):
            remote_addr = environ['HTTP_X_FORWARDED_FOR']
        elif environ.get('REMOTE_ADDR'):
            remote_addr = environ['REMOTE_ADDR']
        d = {
            'REMOTE_ADDR': remote_addr,
            'REMOTE_USER': environ.get('REMOTE_USER') or '-',
            'REQUEST_METHOD': method,
            'REQUEST_URI': req_uri,
            'HTTP_VERSION': environ.get('SERVER_PROTOCOL'),
            'time': time.strftime('%d/%b/%Y:%H:%M:%S ', start) + offset,
            'status': status.split(None, 1)[0],
            'bytes': bytes,
            'HTTP_REFERER': environ.get('HTTP_REFERER', '-'),
            'HTTP_USER_AGENT': environ.get('HTTP_USER_AGENT', '-'),
            }
->      message = self.format % d
        self.logger.log(self.logging_level, message)

In Germany, it happens precisely only in march, as it is the only month that contains an unicode character ä, but more on that in a moment.

The contents of d is the following for me:

{'HTTP_REFERER': '-',
 'HTTP_USER_AGENT': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36',
 'HTTP_VERSION': 'HTTP/1.1',
 'REMOTE_ADDR': '127.0.0.1',
 'REMOTE_USER': u'admin',
 'REQUEST_METHOD': 'GET',
 'REQUEST_URI': '/',
 'bytes': '3222',
 'status': '200',
 'time': '11/M\xc3\xa4r/2023:11:48:46 +0200'}

and self.format is this:

'%(REMOTE_ADDR)s - %(REMOTE_USER)s [%(time)s] "%(REQUEST_METHOD)s %(REQUEST_URI)s %(HTTP_VERSION)s" %(status)s %(bytes)s "%(HTTP_REFERER)s" "%(HTTP_USER_AGENT)s"'

Now the funny part. While this works:

>>> '%s' % time.strftime('%d/%b/%Y:%H:%M:%S ', start) + offset
'11/M\xc3\xa4r/2023:11:48:46 +0200'

this does not:

'%(REMOTE_ADDR)s - %(REMOTE_USER)s [%(time)s]' % {'REMOTE_ADDR': remote_addr, 'REMOTE_USER': environ.get('REMOTE_USER') or '-', 'time': time.strftime('%d/%b/%Y:%H:%M:%S ', start) + offset,}
*** UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 4: ordinal not in range(128)

So the thing that makes it fail is in this case the REMOTE_USER that comes in as a unicode string:

>>> environ.get('REMOTE_USER')
u'admin'

This seems to convert the whole thing into unicode and causes the error.

Once again:

>>> '%(REMOTE_ADDR)s - %(REMOTE_USER)s [%(time)s]' % {'REMOTE_ADDR': remote_addr, 'REMOTE_USER':  u'admin', 'time': time.strftime('%d/%b/%Y:%H:%M:%S ', start) + offset,}
*** UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 4: ordinal not in range(128)

and here the user as a string:

'%(REMOTE_ADDR)s - %(REMOTE_USER)s [%(time)s]' % {'REMOTE_ADDR': remote_addr, 'REMOTE_USER':  'admin', 'time': time.strftime('%d/%b/%Y:%H:%M:%S ', start) + offset,}
'127.0.0.1 - admin [11/M\xc3\xa4r/2023:11:48:46 +0200]'

Because of the the time being encoded already as UTF8, this makes the whole thing break in March if you are in Germany März or in Barcelona (Catalan) març.

On the other 11 months it does not happen...

However, what we noted is that the user seems to not always come in as a unicode string. But we did not yet debug it into detail.

Hopefully we find it out and create a PR for it.

Cheers, Ramon

Some more findings why the REMOTE_USER is once set as a string and the other time as unicode.

We inspected publish_module method in Zope-4.8.7-py2.7.egg/ZPublisher/WSGIPublisher.py where the environ['REMOTE_USER'] is set:

        for i in range(getattr(new_request, 'retry_max_count', 3) + 1):
            request = new_request
            response = new_response
            setRequest(request)
            try:
                with load_app(module_info) as new_mod_info:
                    with transaction_pubevents(request, response):
                        response = _publish(request, new_mod_info)

                        user = getSecurityManager().getUser()
                        if user is not None and \
                           user.getUserName() != 'Anonymous User':
->                          environ['REMOTE_USER'] = user.getUserName()
                break

Once it is set as unicode:

>>> user.getUserName()
u'admin'

And the other time not:

>>> user.getUserName()
'admin'

The reason for this is how you logged in.
If you log in via Basic Auth, the username will be returned as unicode.
If you log in via Plone (Cookie Auth), the username will be returned as str.

The question now would be, where to fix it?

I guess on both sides.

In my opinion, the getSecurityManager().getUser() should always return unicode and the translogger should always encode the user to UTF8.

Any other suggestions?