400 on by-hash URLs when multiple artifacts match

Another interesting failure: https://debusine.debian.net/debian/r-stefanor-dh-python/artifact/2924813/

10s Get:14 http://deb.debian.org/debian sid/non-free amd64 Packages [131 kB]
23
10s Get:15 http://deb.debusine.debian.net/debian/r-stefanor-dh-python sid-dh-python/main amd64 Packages [21.2 kB]
24
10s Get:11 http://deb.debusine.debian.net/debian/r-stefanor-dh-python sid-dh-python/main all Packages [19.2 kB]
25
10s Err:11 http://deb.debusine.debian.net/debian/r-stefanor-dh-python sid-dh-python/main all Packages
26
10s 400 Bad Request [IP: 2a01:4f8:c0c:198f::1 80]
27
10s File has unexpected size (19840 != 19240). Mirror sync in progress? [IP: 2a01:4f8:c0c:198f::1 80]
28
10s Hashes of expected file:
29
10s - Filesize:19240 [weak]
30
10s - SHA256:717a51abcd54ca1981c0fb06f4a2e8664dbe05c3af45ac311712b00cddc88ce4
31
10s Release file created at: Tue, 23 Dec 2025 20:09:25 +0000
32
11s Fetched 22.2 MB in 1s (17.5 MB/s)
33
11s Reading package lists...
34

Why is it not using by-hash? Well it tried to, and failed, because https://deb.debusine.debian.net/debian/r-stefanor-dh-python/dists/sid-dh-python/main/binary-amd64/by-hash/SHA256/48fb59d1bc87562b8a0a707ac09beb805aefdb8937f6c5dd575a60f873d4c857 throws:

2025-12-23 21:52:55,018 [exceptions/2380684/140401774147264] ERROR: Server exception. status_code: 400 detail: None traceback: Traceback (most recent call last):\n  File "/usr/lib/python3/dist-packages/rest_framework/views.py", line 512, in dispatch\n    response = handler(request, *args, **kwargs)\n  File "/usr/lib/python3/dist-packages/debusine/web/archives/views.py", line 291, in get\n    file_in_artifact = get_object_or_404(self.get_queryset())\n  File "/usr/lib/python3/dist-packages/django/shortcuts.py", line 85, in get_object_or_404\n    return queryset.get(*args, **kwargs)\n           ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^\n  File "/usr/lib/python3/dist-packages/django/db/models/query.py", line 640, in get\n    raise self.model.MultipleObjectsReturned(\n    ...<5 lines>...\n    )\ndebusine.db.models.artifacts.FileInArtifact.MultipleObjectsReturned: get() returned more than one FileInArtifact -- it returned 2!\n

Reformatted:

Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/rest_framework/views.py", line 512, in dispatch
    response = handler(request, *args, **kwargs)
  File "/usr/lib/python3/dist-packages/debusine/web/archives/views.py", line 291, in get
    file_in_artifact = get_object_or_404(self.get_queryset())
  File "/usr/lib/python3/dist-packages/django/shortcuts.py", line 85, in get_object_or_404
    return queryset.get(*args, **kwargs)
           ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3/dist-packages/django/db/models/query.py", line 640, in get
    raise self.model.MultipleObjectsReturned(
    ...<5 lines>...
    )
debusine.db.models.artifacts.FileInArtifact.MultipleObjectsReturned: get() returned more than one FileInArtifact -- it returned 2!

It's quite plausible that after updating indices, a Packages file hasn't changed at all. So the same file will appear in multiple artifacts.