None of the answers here seem to explain how you can get to the solution yourself, so I took on a journey, in my case inspecting do-release-upgrade
in KDE Neon on Ubuntu 18.04 LTS.
First, I ran it with tracefile -w
and discovered that the actual release-upgrade-scripts were downloaded into a /tmp/ubuntu-release-upgrader-xxxxxxxx
directory.
Using grep
in that directory, I found the error message in DistUpgradeController.py
:
❯ grep --line-number --recursive --binary-files=without-match "python3 install is corrupted"
DistUpgradeController.py:426: _("Your python3 install is corrupted. "
So I inspected the surrounding code, which used the function _pythonSymlinkCheck
, jumped to that and discovered the root of the problem:
The script expected the symlink /usr/bin/python3
to resolve to exactly /usr/bin/<debian_default_python>
:
binaries_and_dirnames = [("python3", "python3")]
for binary, dirname in binaries_and_dirnames:
debian_defaults = '/usr/share/%s/debian_defaults' % dirname
if os.path.exists(debian_defaults):
config = SafeConfigParser()
with open(debian_defaults) as f:
config.readfp(f)
try:
expected_default = config.get('DEFAULT', 'default-version')
except NoOptionError:
logging.debug("no default version for %s found in '%s'" %
(binary, config))
return False
try:
fs_default_version = os.readlink('/usr/bin/%s' % binary)
except OSError as e:
logging.error("os.readlink failed (%s)" % e)
return False
if not fs_default_version in (expected_default, os.path.join('/usr/bin', expected_default)):
As visible from the script, <debian_default_python>
is the default-version
key in the DEFAULT
section from /usr/share/python3/debian_defaults
:
❯ cat /usr/share/python3/debian_defaults
[DEFAULT]
# the default python3 version
default-version = python3.6
My link did point to /usr/bin/python3.6
, but via an extra indirection from update-alternatives
, which the script doesn't resolve:
❯ python
Python 3.6.9 (default, Jul 17 2020, 12:50:27)
[GCC 8.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> os.readlink("/usr/bin/python3")
'/etc/alternatives/python3'
>>> os.readlink("/etc/alternatives/python3")
'/usr/bin/python3.6'
So in the end I also resorted to the nuclear option, but now with full knowledge of what was going on :)
sudo ln -sf /usr/bin/python3.6 /usr/bin/python3