There is a race condition here that cannot be fixed: onError/onComplete can be sent concurrently with the cancelation, in which case the Subscriber might or might not be notified of the stream termination depending on who won the race.

Perhaps this can be clarified in the wording: “Subscriptions which have been canceled should not receive subsequent `onError` or `onComplete` events, but implementations will not be able to strictly guarantee this in all cases due to the intrinsic race condition between actions taken concurrently by Publisher and Subscriber.”

Side remark: a volatile boolean will not fix this, you would need a (blocking) lock, wherefore mandating this behavior would violate the spirit of the specification.