Repository: PennyLaneAI/pennylane (python)

Issue:
[BUG] Using qml.apply on measurementProcess separated by commas returns ()
### Expected behavior

I am trying to use qml.apply to write test cases, specifically to parametrize across many different measurementProcesses. Some of these MPs look something like this:

```
dev = qml.device("default.qubit", wires=2, shots=1024)

op = qml.sample(qml.X(0) + qml.Y(0)), qml.sample(qml.X(0))
@qml.qnode(dev)
def circuit():
    qml.RX(np.pi / 3, 0)
    qml.RZ(np.pi / 3, 0)
    return qml.apply(op)

circuit()
```

This circuit should raise an error.

### Actual behavior

It returns `()` without any indication of anything going wrong.

### Additional information

_No response_

### Source code

_No response_

### Tracebacks

_No response_

### System information

```shell
Name: PennyLane
Version: 0.37.0.dev0
Summary: PennyLane is a cross-platform Python library for quantum computing, quantum machine learning, and quantum chemistry. Train a quantum computer the same way as a neural network.
Home-page: https://github.com/PennyLaneAI/pennylane
Author: 
Author-email: 
License: Apache License 2.0
Location: /Users/austin.huang/pennylane/pennylane-qiskit/venv/lib/python3.11/site-packages
Requires: appdirs, autograd, autoray, cachetools, networkx, numpy, packaging, pennylane-lightning, requests, rustworkx, scipy, semantic-version, toml, typing-extensions
Required-by: PennyLane_Lightning

Platform info:           macOS-14.5-arm64-arm-64bit
Python version:          3.11.9
Numpy version:           1.26.4
Scipy version:           1.13.1
Installed devices:
- qiskit.aer (PennyLane-qiskit-0.35.0.dev0)
- qiskit.basicaer (PennyLane-qiskit-0.35.0.dev0)
- qiskit.ibmq (PennyLane-qiskit-0.35.0.dev0)
- qiskit.ibmq.circuit_runner (PennyLane-qiskit-0.35.0.dev0)
- qiskit.ibmq.sampler (PennyLane-qiskit-0.35.0.dev0)
- qiskit.remote (PennyLane-qiskit-0.35.0.dev0)
- default.clifford (PennyLane-0.37.0.dev0)
- default.gaussian (PennyLane-0.37.0.dev0)
- default.mixed (PennyLane-0.37.0.dev0)
- default.qubit (PennyLane-0.37.0.dev0)
- default.qubit.autograd (PennyLane-0.37.0.dev0)
- default.qubit.jax (PennyLane-0.37.0.dev0)
- default.qubit.legacy (PennyLane-0.37.0.dev0)
- default.qubit.tf (PennyLane-0.37.0.dev0)
- default.qubit.torch (PennyLane-0.37.0.dev0)
- default.qutrit (PennyLane-0.37.0.dev0)
- default.qutrit.mixed (PennyLane-0.37.0.dev0)
- default.tensor (PennyLane-0.37.0.dev0)
- null.qubit (PennyLane-0.37.0.dev0)
- lightning.qubit (PennyLane_Lightning-0.36.0)
```


### Existing GitHub issues

- [X] I have searched existing GitHub issues to make sure the issue does not already exist.

Pull request:
add error on processing queue if object does not have a `_queue_category`
**Context:**

**Description of the Change:**

Adds an error to `qml.queuing.process_queue` if it encounters an object without a `_queue_category` property that sorts it into the operations or measurements queue.

**Benefits:**

An informative error instead of silently ignoring objects in the queue.

**Possible Drawbacks:**

Non-circuit objects can still be placed into an `AnnotatedQueue`.  `qml.apply` still works on non-circuit objects.

We still silently ignore objects with a `_queue_category == None`, like `Hermitian` or `SparseHamiltonian`.  We should fix the queuing behavior of these observables at some point, but we can do that later.

**Related GitHub Issues:**

Fixes #5865 , [sc-66169]

Patch:
diff --git a/doc/code/qml_queuing.rst b/doc/code/qml_queuing.rst
index 841e600cb..13e48d62b 100644
--- a/doc/code/qml_queuing.rst
+++ b/doc/code/qml_queuing.rst
@@ -15,4 +15,4 @@ Overview
     :no-heading:
     :include-all-objects:
     :no-inherited-members:
-    :skip: contextmanager, OrderedDict, RLock
+    :skip: contextmanager, OrderedDict, RLock, Optional, Union
diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index 229a1aa5c..f561a5864 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -376,6 +376,10 @@
 
 <h3>Bug fixes 🐛</h3>
 
+* An error is now raised on processing an `AnnotatedQueue` into a `QuantumScript` if the queue
+  contains something other than an `Operator`, `MeasurementProcess`, or `QuantumScript`.
+  [(#5866)](https://github.com/PennyLaneAI/pennylane/pull/5866)
+
 * Fixes a bug in the wire handling on special controlled ops.
   [(#5856)](https://github.com/PennyLaneAI/pennylane/pull/5856)
 
diff --git a/pennylane/queuing.py b/pennylane/queuing.py
index f25329baa..ffb35bd91 100644
--- a/pennylane/queuing.py
+++ b/pennylane/queuing.py
@@ -164,7 +164,7 @@ import copy
 from collections import OrderedDict
 from contextlib import contextmanager
 from threading import RLock
-from typing import Optional
+from typing import Optional, Union
 
 
 class QueuingError(Exception):
@@ -551,8 +551,13 @@ def apply(op, context=QueuingManager):
     return op
 
 
+ops_or_meas = Union["pennylane.operation.Operator", "pennylane.measurements.MeasurementProcess"]
+
+
 # pylint: disable=protected-access
-def process_queue(queue: AnnotatedQueue):
+def process_queue(
+    queue: AnnotatedQueue,
+) -> tuple[list[ops_or_meas], list["pennylane.measurements.MeasurementProcess"]]:
     """Process the annotated queue, creating a list of quantum
     operations and measurement processes.
 
@@ -562,13 +567,24 @@ def process_queue(queue: AnnotatedQueue):
     Returns:
         tuple[list(.Operation), list(.MeasurementProcess)]:
         The list of tape operations, the list of tape measurements
+
+    Raises:
+        QueuingError: if the queue contains objects that cannot be processed into a QuantumScript
+
     """
     lists = {"_ops": [], "_measurements": []}
     list_order = {"_ops": 1, "_measurements": 2}
     current_list = "_ops"
 
-    for obj, info in queue.items():
-        if "owner" not in info and getattr(obj, "_queue_category", None) is not None:
+    # cant use for obj in queue.queue, as OperatorRecorder overrides the definition of queue
+    # cant use for obj in queue, as QuantumTape overrides the definition of __iter__
+    for obj, _ in queue.items():
+        if not hasattr(obj, "_queue_category"):
+            raise QueuingError(
+                f"{obj} encountered in AnnotatedQueue and is not an object that can "
+                "be processed into a QuantumScript. Queues should contain Operator or MeasurementProcess objects only."
+            )
+        if obj._queue_category is not None:
             if list_order[obj._queue_category] > list_order[current_list]:
                 current_list = obj._queue_category
             elif list_order[obj._queue_category] < list_order[current_list]:


Test patch:
diff --git a/tests/test_queuing.py b/tests/test_queuing.py
index ad3c80638..ec4bf18ef 100644
--- a/tests/test_queuing.py
+++ b/tests/test_queuing.py
@@ -451,16 +451,9 @@ class TestApplyOp:
                 op1 = qml.apply(5)
                 op2 = qml.apply(6, q1)
 
-            tape2 = qml.tape.QuantumScript.from_queue(q2)
-        tape1 = qml.tape.QuantumScript.from_queue(q1)
         assert q1.queue == [op2]
         assert q2.queue == [op1]
 
-        # note that tapes don't know how to process integers,
-        # so they are not included after queue processing
-        assert tape1.operations == []
-        assert tape2.operations == []
-
 
 class TestWrappedObj:
     """Tests for the ``WrappedObj`` class"""
@@ -516,3 +509,13 @@ class TestWrappedObj:
         obj = Dummy()
         wo = WrappedObj(obj)
         assert wo.__repr__() == "Wrapped(test_repr)"
+
+
+def test_process_queue_error_if_not_operator_or_measurement():
+    """Test that a QueuingError is raised if process queue encounters an object that does not have a
+    _queue_category property
+    """
+    q = AnnotatedQueue()
+    q.append(1)
+    with pytest.raises(QueuingError, match="not an object that can be processed"):
+        qml.queuing.process_queue(q)