In all of my research, I've found that the answer appears to be "no." There's simply no flag or other field you can use to reliably detect this duplicate. The Opportunity Contact Role is created during the transaction finalization, long after your triggers execute. Asynchronous is probably your best option to avoid this problem.
I don't think you will be able to do this via Validation Rule unless you have a field on Opportunity that allows you to know if there are Contact Roles associated.
As a suggestion you may take a look into the addError() sObject method and try to implement this using a trigger.