The error manifested itself in a few ways, the most common being an 'access-denied' thrown by the Sitecore API every time you would click on the 'preview' button.
The context site and database would be resolved properly. The full path of the item, however, looked like this - [orphan]/content/home/page-1. The /sitecore item was null!
To make things harder to debug, the project had a lot of framework customizations, a couple of modules installed, and we were dealing with an upgraded database. In the end, we found that the issue was caused by the 'Hide version' field being checked on the /sitecore item. This is easily reproducible on a clean Sitecore (we tried 22.214.171.124221). Why you would change anything on the /sitecore item is a different topic.
The approach we took in troubleshooting after we gave up debugging was fairly straight-forward. Install a clean Sitecore of the same version as the project and start adding things to it slowly to see where and when it will break. By doing so, we were able to determine that the problem was not caused by a bug in code and was hiding somewhere in the database. Using TDS (Team Development for Sitecore), we actually migrated all project items as well as all code to the clean instance, and preview was still working properly. Finally, by comparing the values of all fields (shared, versioned, and unversioned) for the /sitecore item in the clean master database vs. the problem master database, we found the difference for the 'Hide version' field. Sigh of relief.