diff --git a/qiskit/qpy/interface.py b/qiskit/qpy/interface.py index a22ce5f20db3..aaeb3417b287 100644 --- a/qiskit/qpy/interface.py +++ b/qiskit/qpy/interface.py @@ -225,6 +225,11 @@ def load( file_obj.read(formats.FILE_HEADER_SIZE), ) ) + if data.qpy_version > common.QPY_VERSION: + raise QiskitError( + f"The QPY format version being read, {data.qpy_version}, isn't supported by " + "this Qiskit version. Please upgrade your version of Qiskit to load this QPY payload" + ) if data.preface.decode(common.ENCODE) != "QISKIT": raise QiskitError("Input file is not a valid QPY file") version_match = VERSION_PATTERN_REGEX.search(__version__) diff --git a/releasenotes/notes/fix-error-message-qpy-version-cf0763da22ce2224.yaml b/releasenotes/notes/fix-error-message-qpy-version-cf0763da22ce2224.yaml new file mode 100644 index 000000000000..67690c011126 --- /dev/null +++ b/releasenotes/notes/fix-error-message-qpy-version-cf0763da22ce2224.yaml @@ -0,0 +1,9 @@ +--- +fixes: + - | + Fixed an issue with :func:`.qpy.load` when attempting to load a QPY format + version that is not supported by this version of Qiskit it will now display + a descriptive error message. Previously, it would raise an internal error + because of the incompatibility between the formats which was difficult to + debug. If the QPY format verison is not supported that indicates the Qiskit + version will need to be upgraded to read the QPY payload. diff --git a/test/python/qpy/test_circuit_load_from_qpy.py b/test/python/qpy/test_circuit_load_from_qpy.py index 3684e8aacfe6..ba6aeb81f290 100644 --- a/test/python/qpy/test_circuit_load_from_qpy.py +++ b/test/python/qpy/test_circuit_load_from_qpy.py @@ -13,12 +13,15 @@ """Test cases for the schedule block qpy loading and saving.""" import io +import struct from ddt import ddt, data from qiskit.circuit import QuantumCircuit, QuantumRegister, Qubit from qiskit.providers.fake_provider import FakeHanoi, FakeSherbrooke -from qiskit.qpy import dump, load +from qiskit.exceptions import QiskitError +from qiskit.qpy import dump, load, formats +from qiskit.qpy.common import QPY_VERSION from qiskit.test import QiskitTestCase from qiskit.transpiler import PassManager, TranspileLayout from qiskit.transpiler import passes @@ -71,6 +74,20 @@ def test_rzx_calibration_echo(self, angle): self.assert_roundtrip_equal(rzx_qc) +class TestVersions(QpyCircuitTestCase): + """Test version handling in qpy.""" + + def test_invalid_qpy_version(self): + """Test a descriptive exception is raised if QPY version is too new.""" + with io.BytesIO() as buf: + buf.write( + struct.pack(formats.FILE_HEADER_PACK, b"QISKIT", QPY_VERSION + 4, 42, 42, 1, 2) + ) + buf.seek(0) + with self.assertRaisesRegex(QiskitError, str(QPY_VERSION + 4)): + load(buf) + + @ddt class TestLayout(QpyCircuitTestCase): """Test circuit serialization for layout preservation.""" diff --git a/test/qpy_compat/process_version.sh b/test/qpy_compat/process_version.sh index 59c5610eb95c..c575317fd0b1 100755 --- a/test/qpy_compat/process_version.sh +++ b/test/qpy_compat/process_version.sh @@ -18,6 +18,8 @@ version=$1 parts=( ${version//./ } ) if [[ ${parts[1]} -lt 18 ]] ; then exit 0 +elif [[ ${parts[1]} -gt 25 ]] ; then + exit 0 fi if [[ ! -d qpy_$version ]] ; then