#1051852 python-apt does not recognise 'signed-by' parameter

Package:
python3-apt
Source:
python3-apt
Description:
Python 3 interface to libapt-pkg
Submitter:
zzaimeche
Date:
2026-10-22 11:05:02 UTC
Severity:
normal
#1051852#5
Date:
2023-09-13 16:27:13 UTC
From:
To:
The current code does not handle sources which include the 'signed-by'
option.
These are interpreted as invalid, although signed-by is listed as a
valid
option for ubuntu source lists here:
https://manpages.ubuntu.com/manpages/focal/en/man5/sources.list.5.html .

I'm not sure whether it's supported for debian or not,
but I couldn't find a better place to report the issue,
so hopefully this is the right place! :)

When 'signed-by' is not recognised, it causes knock-on effects,
for example when using salt to add repos to /etc/apt/sources.list,
there will be duplicate entries.

This is where the 'signed-by' parameter would need to be added:
https://salsa.debian.org/apt-team/python-apt/-/blob/main/aptsources/sourceslist.py#L240

And below is a patch that has fixed the issue for us
(it's a little old so we may need to adjust the line numbers, but the
rest should be the same).
Best Wishes,
Zara

@@ -, +, @@
---
  aptsources/sourceslist.py | 27 ++++++++++++++++++++-------
  1 file changed, 20 insertions(+), 7 deletions(-)
--- a/aptsources/sourceslist.py
+++ a/aptsources/sourceslist.py
@@ -92,6 +92,7 @@ class SourceEntry(object):
          self.type = ""               # what type (deb, deb-src)
          self.architectures = []      # architectures
          self.trusted = None          # Trusted
+        self.signed_by = ""          # gpg
          self.uri = ""                # base-uri
          self.dist = ""               # distribution (dapper, edgy, etc)
          self.comps = []              # list of available componetns
(may empty)
@@ -198,6 +199,8 @@ class SourceEntry(object):
                          self.architectures = value.split(",")
                      elif key == "trusted":
                          self.trusted = apt_pkg.string_to_bool(value)
+                    elif key == "signed-by":
+                        self.signed_by = value
                      else:
                          self.invalid = True
@@ -239,13 +242,23 @@ class SourceEntry(object):
          line += self.type
-        if self.architectures and self.trusted is not None:
-            line += " [arch=%s trusted=%s]" % (
-                ",".join(self.architectures), "yes" if self.trusted
else "no")
-        elif self.trusted is not None:
-            line += " [trusted=%s]" % ("yes" if self.trusted else "no")
-        elif self.architectures:
-            line += " [arch=%s]" % ",".join(self.architectures)
+        repo_opts = {}
+
+        if self.architectures:
+            repo_opts["arch"] = ",".join(self.architectures)
+
+        if self.trusted is not None:
+            repo_opts["trusted"] = "yes" if self.trusted else "no"
+
+        if self.signed_by:
+            repo_opts["signed-by"] = self.signed_by
+
+        if repo_opts:
+            _opts_str = ""
+            for key in repo_opts:
+                _opts_str += "%s=%s " % (key, repo_opts[key])
+            line += " [%s]" % _opts_str.strip()
+
          line += " %s %s" % (self.uri, self.dist)
          if len(self.comps) > 0:
              line += " " + " ".join(self.comps)
--

#1051852#10
Date:
2026-10-22 11:02:28 UTC
From:
To:
Hi,

A further consequence of this bug: Ubuntu's release upgrader uses
aptsources.sourceslist.SourceEntry to migrate one-line .list sources to
deb822 .sources. Because [signed-by=...] lines parse as invalid, it cannot
convert them. It writes them, commented out, to foo.list.disabled and
removes them from foo.list (deleting the file, or leaving only its comment
lines). apt then has no source for those packages. The upgrader logs this
only at DEBUG level, and apt update and apt upgrade report nothing.

signed-by is the recommended way to configure third-party repositories, so
it is the correctly configured ones that get disabled.

Seen on this machine during two release upgrades (to 26.04 and then 26.10).
Excerpt from the upgrader's log (/var/log/dist-upgrade/main.log) for the
26.10 upgrade, timestamps removed:

DEBUG Could not migrate deb
[signed-by=/usr/share/keyrings/cloud.google.gpg]
https://packages.cloud.google.com/apt cloud-sdk main from
/etc/apt/sources.list.d/google-cloud-sdk.list, disabling instead.
DEBUG Could not migrate deb
[signed-by=/usr/share/keyrings/tailscale-archive-keyring.gpg]
https://pkgs.tailscale.com/stable/ubuntu resolute main from
/etc/apt/sources.list.d/tailscale.list, disabling instead.

The earlier upgrade to 26.04 disabled a third repository the same way (its
.list.disabled file has the same header). Before that upgrade, dpkg.log
shows its package being upgraded every few days. The last upgrade was the
day before the release upgrade, and the next was 11 weeks later, after I
restored the source by hand.

Reproducer, on Ubuntu 26.10 with python3-apt 3.1.0ubuntu2:

$ /usr/bin/python3 -c '
from aptsources.sourceslist import SourceEntry
print(SourceEntry("deb [signed-by=/usr/share/keyrings/x.gpg]
https://example.com/apt stable main").invalid)
print(SourceEntry("deb [arch=amd64] https://example.com/apt stable
main").invalid)
print(SourceEntry("deb https://example.com/apt stable main").invalid)
'
True
False
False

[arch=] parses, but [signed-by=] does not.

The upgrader acknowledges the cause in a comment in
DistUpgrade/DistUpgradeController.py, migrateToDeb822Sources(), in
ubuntu-release-upgrader 1:26.10.11:

# It's possible that the source is considered invalid because python-apt
# does not support [signed-by=] or something. For anything that we
# cannot migrate, move it instead to <filename>.disabled. These will not
# be cleaned up at the end of the upgrade.

Fixing signed-by parsing in SourceEntry, along the lines of the patch
already attached to this report, should let these entries migrate normally.
The upgrader's handling of entries it cannot parse (removing them from the
original file, logging only at DEBUG) is a separate Ubuntu issue.

Versions (current, after the 26.10 upgrade):
Ubuntu 26.10 (stonking)
python3-apt 3.1.0ubuntu2
ubuntu-release-upgrader-core 1:26.10.11
apt 3.3.3
The 26.04 upgrade ran release-upgrader 26.04.22.

Thanks,
Ove Brynestad