diff --git a/gnu/local.mk b/gnu/local.mk
index 741c72a653..b10063678e 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -1943,6 +1943,7 @@ dist_patch_DATA =						\
   %D%/packages/patches/python-versioneer-guix-support.patch	\
   %D%/packages/patches/python-waitress-fix-tests.patch		\
   %D%/packages/patches/python-werkzeug-tests.patch		\
+  %D%/packages/patches/python-xmp-toolkit-add-missing-error-codes.patch	\
   %D%/packages/patches/python-zeep-Fix-pytest_httpx-test-cases.patch	\
   %D%/packages/patches/qemu-build-info-manual.patch		\
   %D%/packages/patches/qemu-disable-some-qtests-tests.patch	\
diff --git a/gnu/packages/patches/python-xmp-toolkit-add-missing-error-codes.patch b/gnu/packages/patches/python-xmp-toolkit-add-missing-error-codes.patch
new file mode 100644
index 0000000000..17bdae188f
--- /dev/null
+++ b/gnu/packages/patches/python-xmp-toolkit-add-missing-error-codes.patch
@@ -0,0 +1,51 @@
+From fd9e887a1853d9b4bc3fce20d414d648fb5abbfc Mon Sep 17 00:00:00 2001
+From: Asher Glick <asher.glick@gmail.com>
+Date: Mon, 1 Mar 2021 14:35:46 -0600
+Subject: [PATCH] Add missing error codes and graceful handling of possible
+ future missing codes.
+
+---
+ libxmp/exempi.py | 14 +++++++++++++-
+ 1 file changed, 13 insertions(+), 1 deletion(-)
+
+diff --git a/libxmp/exempi.py b/libxmp/exempi.py
+index 683e240..f58003c 100644
+--- a/libxmp/exempi.py
++++ b/libxmp/exempi.py
+@@ -85,6 +85,7 @@ ERROR_MESSAGE = {    0: "unknown error",
+                    -13: "std exception",
+                    -14: "unknown exception",
+                    -15: "no memory",
++                   -16: "progress abort",
+                   -101: "bad schema",
+                   -102: "bad XPath",
+                   -103: "bad options",
+@@ -95,6 +96,14 @@ ERROR_MESSAGE = {    0: "unknown error",
+                   -108: "bad file format",
+                   -109: "no file handler",
+                   -110: "too large for JPEG",
++                  -111: "no file",
++                  -112: "file permission error",
++                  -113: "disk space",
++                  -114: "read error",
++                  -115: "write error",
++                  -116: "bad block format",
++                  -117: "file path not a file",
++                  -118: "rejected file extension",
+                   -201: "bad XML",
+                   -202: "bad RDF",
+                   -203: "bad XMP",
+@@ -1697,6 +1706,9 @@ def check_error(success):
+     # so we supplement it by explicitly checking the error code.
+     ecode = EXEMPI.xmp_get_error()
+     if not success or ecode != 0:
+-        error_msg = ERROR_MESSAGE[ecode]
++        if ecode in ERROR_MESSAGE:
++            error_msg = ERROR_MESSAGE[ecode]
++        else:
++            error_msg = "Unexpected error code " + str(ecode)
+         msg = 'Exempi function failure ("{0}").'.format(error_msg)
+         raise XMPError(msg)
+-- 
+2.30.2
+
diff --git a/gnu/packages/python-xyz.scm b/gnu/packages/python-xyz.scm
index a051f5a8db..6cbedcb5e9 100644
--- a/gnu/packages/python-xyz.scm
+++ b/gnu/packages/python-xyz.scm
@@ -108,7 +108,7 @@
 ;;; Copyright © 2021 Simon Streit <simon@netpanic.org>
 ;;; Copyright © 2021, 2022, 2023 Daniel Meißner <daniel.meissner-i4k@ruhr-uni-bochum.de>
 ;;; Copyright © 2021, 2022 Pradana Aumars <paumars@courrier.dev>
-;;; Copyright © 2021, 2022, 2023 Felix Gruber <felgru@posteo.net>
+;;; Copyright © 2021–2024 Felix Gruber <felgru@posteo.net>
 ;;; Copyright © 2021 Sébastien Lerique <sl@eauchat.org>
 ;;; Copyright © 2021 Raphaël Mélotte <raphael.melotte@mind.be>
 ;;; Copyright © 2021 ZmnSCPxj <ZmnSCPxj@protonmail.com>
@@ -35628,6 +35628,55 @@ (define-public python-zeroc-ice-3.6
                (base32
                 "0mikjfvq26kh8asnn9v55z41pap4c5ypymqnwwi4xkavc3mzyda2"))))))
 
+(define-public python-xmp-toolkit
+  (package
+    (name "python-xmp-toolkit")
+    (version "2.0.2")
+    (source
+     (origin
+       (method url-fetch)
+       (uri (pypi-uri "python-xmp-toolkit" version))
+       (sha256
+        (base32 "12x6lyaxjpbl8ll3cj97039kwvsha2nkx2v8v8irfbi2p0dl721s"))
+        (patches (search-patches
+                   "python-xmp-toolkit-add-missing-error-codes.patch"))))
+    (build-system pyproject-build-system)
+    (arguments
+     (list
+      #:test-flags
+      '(list "-k" (string-append "not test_can_put_xmp and "
+                                 "not test_exempi_bad_combinations and "
+                                 "not test_formats and "
+                                 "not test_get_xmp and "
+                                 "not test_open_file_with_options"))
+      #:phases
+      #~(modify-phases %standard-phases
+          (add-before 'build 'configure-environment
+            (lambda* (#:key outputs inputs #:allow-other-keys)
+              (let ((exempi #$(this-package-input "exempi")))
+                (setenv "LD_LIBRARY_PATH"
+                        (string-append exempi "/lib"))))))))
+    (inputs (list exempi))
+    (propagated-inputs (list python-pytz))
+    (native-inputs (list python-pytest))
+    (home-page "https://github.com/python-xmp-toolkit/python-xmp-toolkit")
+    (synopsis "Python XMP Toolkit for working with metadata.")
+    (description "Python XMP Toolkit is a library for working with XMP
+metadata, as well as reading/writing XMP metadata stored in many different
+file formats.
+
+Python XMP Toolkit is wrapping Exempi (using ctypes), a C/C++ XMP library
+based on Adobe XMP Toolkit, ensuring that future updates to the XMP standard
+are easily incorporated into the library with a minimum amount of work.
+
+Python XMP Toolkit has been developed by:
+@itemize
+@item ESA/Hubble - European Space Agency
+@item ESO - European Southern Observatory
+@item CRS4 - Centre for Advanced Studies, Research and Development in Sardinia
+@end itemize")
+      (license license:bsd-3)))
+
 ;;;
 ;;; Avoid adding new packages to the end of this file. To reduce the chances
 ;;; of a merge conflict, place them above by existing packages with similar
