[go: up one dir, main page]

Skip to content

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

  1. Prepare a multi.json with this content:
[
  {
    "description": "Backend 1 - success",
    "url": "file:///tmp/success/"
  },
  {
    "description": "Backend 2 - fails",
    "url": "file:///nonexistent/"
  }
]
  1. 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 >:

https://gitlab.com/duplicity/duplicity/-/blob/3bc1bb974c252ceb170b8e3edd8f791af30c719f/duplicity/backend.py#L421

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_volume is never set anywhere
  • BadUploadTestBackend._put_fail_volume is 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.