I just stumbled across this thread. I have been having this exact same problem while using a similar script to back up all of my SQL databases by cursoring over sys.databases. I have a script that runs on about 15 servers and for some reason a couple of them periodically display this behavior. When I stumbled across your post, I decided to figure out what was going on.
As it turns out, what is happening is @@FETCH_STATUS is being set to -2, which implies that "the row fetched is missing". Clearly the record is still in sys.databases, but apparently something is causing the cursor to be invalidated.
In your case, you were only checking for @@FETCH_STATUS <> -1, so you weren't catching this condition. I check for @@FETCH_STATUS = 0, which was previously suggested, and I suggest you do the same.
I haven't fully answered what is causing this yet, though I suspect that the backup operation is changing something in an underlying table as it backs up each database, and depending on the order the records are stored, or something along those lines, the cursor is somehow, sometimes being invalidated.
Like you, I too just did a simple DECLARE CURSOR without any parameters, even though typically if I had to use a cursor I would at least use FAST_FORWARD and READ_ONLY for this type of application. I'm not sure if that would help here, but it's probably worth implementing.
Regardless of the exact cause of the problem, it seems the only sure way to prevent it is to create a copy of the data being cursored over and then work off of that copy. According to Books Online, the "STATIC" keyword does that for you, creating a copy of the data in tempdb. I went ahead and added that to my cursor declaration. Given the randomness of this happening it's too soon to say for sure it's fixed, but hopefully this helps you understand your problem.