From 8abdd3b8bf065dceecd52d2b22d92b3c407a7c1d Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Thu, 18 Jun 2026 18:23:49 +0200 Subject: [PATCH 1/2] support msgpack 1.2.1 --- docs/changes.rst | 2 +- pyproject.toml | 2 +- src/borg/helpers/msgpack.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/changes.rst b/docs/changes.rst index 3eaf24eba..5928735b5 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -473,7 +473,7 @@ Note: Many of the fixed issues listed below relate to rather rare or theoretical Other changes: -- msgpack: allow 1.2.0 +- msgpack: also allow 1.2.0 and 1.2.1 - use F_FULLFSYNC on macOS for SyncFile data durability, #9383 - mount: improve error msg when uid/gid cannot be resolved, #9574 - docs: diff --git a/pyproject.toml b/pyproject.toml index b789bf526..c60d128bd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,7 +35,7 @@ dependencies = [ # Please note: # Using any other msgpack version is not supported by Borg development and # any feedback related to issues caused by this will be ignored. - "msgpack >=1.0.3, <=1.2.0", + "msgpack >=1.0.3, <=1.2.1", "packaging", ] diff --git a/src/borg/helpers/msgpack.py b/src/borg/helpers/msgpack.py index 0adf8c0b0..ae634e2ee 100644 --- a/src/borg/helpers/msgpack.py +++ b/src/borg/helpers/msgpack.py @@ -145,7 +145,7 @@ def is_supported_msgpack(): version_check = os.environ.get('BORG_MSGPACK_VERSION_CHECK', 'yes').strip().lower() return version_check == 'no' or ( - (1, 0, 3) <= msgpack.version[:3] <= (1, 2, 0) and + (1, 0, 3) <= msgpack.version[:3] <= (1, 2, 1) and msgpack.version not in [] ) From b09bbed3de095d6ac9d69a42a486ec18523046dc Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Thu, 18 Jun 2026 19:42:53 +0200 Subject: [PATCH 2/2] Archive.delete: don't reuse msgpack Unpacker after an unpacking failure msgpack does not support reusing an Unpacker instance after an unpacking failure - a new instance must be created instead. In the forced-delete path, item metadata was unpacked via "for item in unpacker" with an "except (TypeError, ValueError)" handler. msgpack's FormatError/StackError are ValueError subclasses, so a real unpacking failure was caught there and the (now broken) unpacker kept being reused for the following chunks, making them all fail too - leaking their chunk references. Restructure the loop to use next(unpacker) so each failure mode gets a tight handler: - StopIteration: buffer drained, feed next chunk - msgpack.UnpackException: corruption; in forced mode replace the unpacker with a fresh instance and skip the rest of the chunk - TypeError/ValueError: only wraps Item processing (bad types yielded) Co-Authored-By: Claude Opus 4.8 --- src/borg/archive.py | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/src/borg/archive.py b/src/borg/archive.py index 0fc411d3f..20cef9f67 100644 --- a/src/borg/archive.py +++ b/src/borg/archive.py @@ -1035,22 +1035,35 @@ Utilization of max. archive size: {csize_max:.0%} data = self.key.decrypt(items_id, data) unpacker.feed(data) chunk_decref(items_id, stats) - try: - for item in unpacker: + while True: + try: + item = next(unpacker) + except StopIteration: + # no more (complete) items in the buffer, feed the next chunk + break + except msgpack.UnpackException: + # items metadata corrupted. the unpacker can't be reused after an + # unpacking failure, so create a fresh one and skip the rest of this chunk. + if forced == 0: + raise + error = True + unpacker = msgpack.Unpacker(use_list=False) + break + try: item = Item(internal_dict=item) if 'chunks' in item: part = not self.consider_part_files and 'part' in item for chunk_id, size, csize in item.chunks: chunk_decref(chunk_id, stats, part=part) - except (TypeError, ValueError): - # if items metadata spans multiple chunks and one chunk got dropped somehow, - # it could be that unpacker yields bad types - if forced == 0: - raise - error = True + except (TypeError, ValueError): + # if items metadata spans multiple chunks and one chunk got dropped somehow, + # it could be that unpacker yields bad types + if forced == 0: + raise + error = True if progress: pi.finish() - except (msgpack.UnpackException, Repository.ObjectNotFound): + except Repository.ObjectNotFound: # items metadata corrupted if forced == 0: raise