I managed to get my code to work on the Friday afternoon and opened my celebratory beer with this warm sense of accomplishment. Life was good. For a while. Monday quickly rolled around as it always does and I found myself looking at a broken application - not a great start of the week; the code gremlins had once again sabotaged my work. My call to SaveBinaryDirect was crashing and a bit of debugging revealed that SharePoint was returning HTTP 500 without any further details (System.Net.WebException: The remote server returned an error: (500) Internal Server Error). What could it be?
Spolier alert: the maximum number of minor versions was being exceeded.
Now let's enjoy the story of my troubleshooting efforts even though I spoiled it. Some stories are worth reading even if you know how they end.
First thing I tried was to upload a different file - no problem with that. Double-checked if the file was checked-out - it wasn't. It was time to dig in the ULS.
If you have verbose logging on CSOM calls are actually easy to troubleshoot, you can see all the log entries for a given request more or less in sequence and if you use UlsViewer, and I don't see why you wouldn't, you can filter for a specific request.
I compared the log entries for the successful and unsuccessful uploads and the only difference was these two lines:
SQL return value 154 as HR 0x8007009a.
Translated store error 0x8007009a to 0x00210088
Now that's positively cryptic and google was of little help. Fortunately, it downed on me to conduct another test before spending too much time on this - I tried to upload the file the regular way, with a FileCreationInformation and batching as opposed to SaveBinaryDirect. Much to my delight, this time SharePoint was kind enough to tell me exactly what it didn't like about my request:
Microsoft.SharePoint.Client.ServerException: Minor version limit exceeded.
You must first publish a major version before editing this document.
In my case the file was being uploaded every 5 minutes, which exhausted the maximum number of 511 minor versions in just about 42 hours, which may or may not have something to do with life, the universe and everything.
I did as advised and now my solution publishes a major version by checking out and back in. The major versions can go up to 400,000 which will give me more than 3 years with an update every 5 minutes - much better though still not ideal, I guess I'll have to timestamp the file in order to avoid all these issues.
I also simplified the story of forcing the major version which is not exactly trivial - one needs to take into account situations like the file being left checked out, both by the same user and by others. If you behave I'll give you the code for this, too.