multi backend with '' fails whole operation if one backend fails
I have:
- ([x] when completed)
-
searched https://gitlab.com/duplicity/duplicity/-/issues for similar issues. If you find a similar issue and the issue is still open, add a comment to the existing issue instead of opening a new one. If you find a Closed issue that seems like it is the same thing that you're experiencing, open a new issue and include a link to the original issue in the body of your new one. -
tested that this issue still occurs on the latest stable snap (install instructions: https://snapcraft.io/duplicity), please include the snap version ( snap info duplicity | grep installed) output:3.0.6.dev3 (639) 146MB classic -
ideally, tested that this issue still occurs on the latest edge snap, if you can test without risking your data. Please include the snap version output: 3.0.6.dev3 (639) 146MB classic
Summary
multi backend with '' fails whole operation if one backend fails
Environment
Debian 12
duplicity 3.0.6.dev3 June 25, 2025
/snap/bin/duplicity --no-enc --no-comp -v DEBUG full ~/Pictures/ "multi://$(pwd)/multi.json?mode=mirror&>"
Steps to reproduce
- Prepare a
multi.jsonwith this content:
[
{
"description": "Backend 1 - success",
"url": "file:///tmp/success/"
},
{
"description": "Backend 2 - fails",
"url": "file:///nonexistent/"
}
]
rm -fr ~/.cache/duplicity/ && /snap/bin/duplicity --no-enc --no-comp -v DEBUG full ~/Pictures/ "multi://$(pwd)/multi.json?mode=mirror&"
What is the current bug behaviour?
duplicity manages to upload one difftar to backend 1, but fails to finalize the backup (no signatures, no manifest):
$ ls -l /tmp/success/
total 1,581,056
-rw-r--r-- 1 catalinp catalinp 1,579,520 Jun 27 18:20 duplicity-full.20250627T222024Z.vol1.difftar
What is the expected correct behaviour?
duplicity uploads a complete backup set to /tmp/success.
Relevant logs and/or screenshots
GPG binary is /usr/bin/gpg, version 2.2.40
Import of duplicity.backends._testbackend Failed
MultiBackend: use store file:///tmp/success/
MultiBackend: use store file:///nonexistent/
Using archive dir: /home/catalinp/.cache/duplicity/354b4a1c940f7d83ae1eb430336756e6
Using backup name: 354b4a1c940f7d83ae1eb430336756e6
Acquiring lockfile /home/catalinp/.cache/duplicity/354b4a1c940f7d83ae1eb430336756e6/lockfile
================================================================================
duplicity 3.0.6.dev3
Args: /snap/duplicity/639//usr/local/duplicity.venv/bin/duplicity --no-enc --no-comp -v DEBUG full /home/catalinp/Pictures/ multi:///<snip>/multi.json?mode=mirror&>
Linux <snip> 6.1.0-37-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.140-1 (2025-05-22) x86_64
/snap/duplicity/639/usr/local/duplicity.venv/bin/python3.12 3.12.3 (main, Jun 18 2025, 17:59:45) [GCC 13.3.0]
================================================================================
Using temporary directory /tmp/duplicity-jui__xjm-tempdir
Registering (mkstemp) temporary file /tmp/duplicity-jui__xjm-tempdir/mkstemp-mbw3o41n-1
Temp has 15,316,340,736 available, backup will use approx 482,344,960.
MultiBackend: file:///tmp/success/: 0 files
MultiBackend: file:///nonexistent/: 0 files
Exception during list of file:///nonexistent/: No such file or directory
MultiBackend: combined list: []
0 file(s) exists on backend
1 file(s) exist in cache
Extracting backup chains from list of files: []
Last full backup date: none
Collection Status
-----------------
Connecting with backend: BackendWrapper
Archive dir: /home/catalinp/.cache/duplicity/354b4a1c940f7d83ae1eb430336756e6
Found 0 secondary backup chain(s).
No backup chains with active signatures found
No orphaned or incomplete backup sets found.
Using temporary directory /home/catalinp/.cache/duplicity/354b4a1c940f7d83ae1eb430336756e6/duplicity-_rnwue9_-tempdir
Registering (mktemp) temporary file /home/catalinp/.cache/duplicity/354b4a1c940f7d83ae1eb430336756e6/duplicity-_rnwue9_-tempdir/mktemp-ryl_f1ge-1
Using temporary directory /home/catalinp/.cache/duplicity/354b4a1c940f7d83ae1eb430336756e6/duplicity-8j9uws3g-tempdir
Registering (mktemp) temporary file /home/catalinp/.cache/duplicity/354b4a1c940f7d83ae1eb430336756e6/duplicity-8j9uws3g-tempdir/mktemp-67qvo8y4-1
Registering (mktemp) temporary file /tmp/duplicity-jui__xjm-tempdir/mktemp-wjt8m22y-2
Selecting /home/catalinp/Pictures
Comparing . and None
Getting delta of (. dir) and None
A .
Selection: examining path /home/catalinp/Pictures/1416201882778.jpg
Selection: + no selection functions found. Including
Selecting /home/catalinp/Pictures/1416201882778.jpg
Comparing 1416201882778.jpg and None
Getting delta of (1416201882778.jpg reg) and None
A 1416201882778.jpg
Selection: examining path /home/catalinp/Pictures/Screenshot from 2024-11-17 14-35-19.png
Selection: + no selection functions found. Including
Selecting /home/catalinp/Pictures/Screenshot from 2024-11-17 14-35-19.png
Comparing Screenshot from 2024-11-17 14-35-19.png and None
Getting delta of (Screenshot from 2024-11-17 14-35-19.png reg) and None
A Screenshot from 2024-11-17 14-35-19.png
Selection: examining path /home/catalinp/Pictures/rand.bin
Selection: + no selection functions found. Including
Selecting /home/catalinp/Pictures/rand.bin
Comparing rand.bin and None
Getting delta of (rand.bin reg) and None
A rand.bin
Removing still remembered temporary file /home/catalinp/.cache/duplicity/354b4a1c940f7d83ae1eb430336756e6/duplicity-_rnwue9_-tempdir/mktemp-ryl_f1ge-1
Removing still remembered temporary file /home/catalinp/.cache/duplicity/354b4a1c940f7d83ae1eb430336756e6/duplicity-8j9uws3g-tempdir/mktemp-67qvo8y4-1
Writing duplicity-full.20250627T222024Z.vol1.difftar
MultiBackend: _put: write to store #0 (file:///tmp/success/)
Writing duplicity-full.20250627T222024Z.vol1.difftar
MultiBackend: _put: write to store #1 (file:///nonexistent/)
Writing duplicity-full.20250627T222024Z.vol1.difftar
Backtrace of previous error: Traceback (innermost last):
File "/snap/duplicity/639/usr/local/duplicity.venv/lib/python3.12/site-packages/duplicity/backend.py", line 376, in inner_retry
return fn(self, *args)
^^^^^^^^^^^^^^^
File "/snap/duplicity/639/usr/local/duplicity.venv/lib/python3.12/site-packages/duplicity/backend.py", line 579, in put
self.__do_put(source_path, remote_filename)
File "/snap/duplicity/639/usr/local/duplicity.venv/lib/python3.12/site-packages/duplicity/backend.py", line 563, in __do_put
self.backend._put(source_path, remote_filename)
File "/snap/duplicity/639/usr/local/duplicity.venv/lib/python3.12/site-packages/duplicity/backends/localbackend.py", line 64, in _put
target_path.writefileobj(source_path.open("rb"))
File "/snap/duplicity/639/usr/local/duplicity.venv/lib/python3.12/site-packages/duplicity/path.py", line 648, in writefileobj
fout = self.open("wb")
^^^^^^^^^^^^^^^
File "/snap/duplicity/639/usr/local/duplicity.venv/lib/python3.12/site-packages/duplicity/path.py", line 589, in open
result = open(self.name, mode)
^^^^^^^^^^^^^^^^^^^^^
FileNotFoundError: [Errno 2] No such file or directory: b'/nonexistent/duplicity-full.20250627T222024Z.vol1.difftar'
Giving up after 1 attempts. FileNotFoundError: No such file or directory
Releasing lockfile /home/catalinp/.cache/duplicity/354b4a1c940f7d83ae1eb430336756e6/lockfile
Removing still remembered temporary file /tmp/duplicity-jui__xjm-tempdir/mkstemp-mbw3o41n-1
Removing still remembered temporary file /tmp/duplicity-jui__xjm-tempdir/mktemp-wjt8m22y-2
Possible fixes
I think the reason is because of where backend 'retry' is implemented:
duplicity core -> multi backend -> @retry("put", fatal=True) -> underlying backend
So when any underlying backend fails, fatal=True and the whole program fatals, even if >:
I guess we need to find a way to make fatal to just report an error to multi (maybe as an exception) and let multi swallow the error if >.
BTW while thinking about how to test this case, I found some unused test scaffolding:
-
config.put_fail_volumeis never set anywhere -
BadUploadTestBackend._put_fail_volumeis unused
Let me know if you want me to send some MR to clean this up. (or if you want to keep them for testing fixes of this issue - though maybe there's simpler ways to make a test for this issue)
Formatting
PLEASE DO NOT copy/paste long listings to the issue. Use the attach file option to attach a file instead. This makes it much easier to read and to process by the developers.