X_STATE_CONFLICT
Means
the row changed after the read this transition was decided on
Typical cause
the lost update, caught: two callers both read pending, both found the move legal, and the second one's statement carried status = 'pending' in its predicate and matched nothing. Or the row is still in from but a column the deciding policy read (move.observed, #702) changed — cause names the column, never its value. The state in cause is read back AFTER the refusal, so it is a diagnosis and never the decision — the decision was the statement, and it was atomic. 409 and not 422: nothing is wrong with the request, the identical one succeeds a moment later, and re-read-and-retry is precisely what a 409 tells a client to do
Fix
re-read the row and decide again against the state cause names: orders.findById(id) — a transition names the state it expects, so a stale read is refused rather than overwritten