PennyLane's errors cluster around one theme more than any other SDK on this list: differentiability. Because PennyLane exists to make quantum circuits trainable, most of the recurring problems are about gradients silently not flowing, rather than the circuit itself being wrong.
Using plain NumPy instead of pennylane.numpy
import numpy as np # wrong for trainable parameters
params = np.array([0.1, 0.2])
qml.grad(circuit)(params) # gradient is zero or errors
PennyLane's autodifferentiation tracks which values are trainable through its own NumPy wrapper. Plain NumPy arrays don't carry that tracking, so a circuit built with them looks like it runs fine but produces a zero or wrong gradient, without an error pointing at the cause. Import PennyLane's wrapper instead:
import pennylane.numpy as np
params = np.array([0.1, 0.2], requires_grad=True)
requires_grad=True is the default for pennylane.numpy arrays, but setting it explicitly makes the intent visible in code review, and it's required if you're mixing trainable and non-trainable parameters in the same array.
WireError: wires not contained in the device
dev = qml.device('default.qubit', wires=2)
@qml.qnode(dev)
def circuit():
qml.Hadamard(wires=2) # device only has wires 0 and 1
return qml.probs(wires=[0, 1])
The device was declared with 2 wires (indices 0 and 1), and the circuit references wire 2, which doesn't exist on it. This happens most often when a circuit gets extended (adding a qubit for an ancilla, for instance) without updating the device declaration to match. Count wires carefully when the circuit changes, or declare the device with a named wire list (wires=['a', 'b', 'ancilla']) so mismatches are easier to spot than off-by-one integer errors.
qml.grad fails or falls back to a slow method unexpectedly
PennyLane picks a differentiation method automatically (diff_method='best' by default), and what's available depends on the device. backprop, the fast option, only works on simulators that support it (like default.qubit in simulation mode), not on shot-based or hardware-backed devices. Running the same circuit against a QPU or a shots-based device silently falls back to parameter-shift, which is slower and needs multiple circuit evaluations per gradient. If gradient computation is suddenly much slower after switching devices, check what diff_method the new device supports rather than assuming 'best' picked the same thing both times, and set it explicitly (diff_method='parameter-shift') when you need consistent, predictable behavior across devices.
Plugin version drift after upgrading
PennyLane's device backends for other SDKs (pennylane-lightning, pennylane-qiskit, pennylane-cirq, pennylane-braket) are separate packages with their own version numbers, not bundled with core pennylane. Upgrading pennylane without upgrading its plugins, or the reverse, produces ImportErrors referencing internal APIs that changed between versions, or silent behavior differences that are much harder to trace. When you upgrade one, check pip list | grep pennylane and upgrade the plugins in the same pass:
pip install --upgrade pennylane pennylane-lightning pennylane-qiskit
QNode return shape doesn't match what you expected
Mixing qml.probs() and qml.expval() in the same QNode, or forgetting that batched/broadcasted inputs change a QNode's output from a single array to a stack of arrays, is a common source of shape-mismatch errors downstream in whatever consumes the QNode's output (an optimizer, a loss function). Print result.shape right after calling the QNode when debugging a training loop that's failing on a shape error further down, rather than assuming the QNode's output shape matches your mental model of the circuit.
The pattern behind most of these
Most PennyLane errors trace back to the same root cause: something about differentiability, either the wrong NumPy, the wrong diff_method for the device, or plugin versions out of sync, broke silently rather than loudly. Our VQE with PennyLane guide builds a full optimization loop with the correct patterns if you want a working reference to check code against.