Are there any known clock skew issues with serial communication between a Raspberry Pi and an Arduino?
It's not something that is going to show up like this. Where it might show up is occasional misreads; if you try to push the Pi over 1 Mbps you may notice this (although I also think the implementation could catch the potential and throw an error, I don't know if it does).
UART timing is done with hardware clocks that may have some miniscule skew relative to one another. However, that potential, miniscule skew is not something that could have a cumulative effect over the course of hours. The clocks run faster than the actual transfer rate; a rule of thumb you may find with regard to implementing a UART interface is that the clock should be at least eight times faster than the bitrate -- it's probably still pretty feasible at four, but less than that and it won't work. Or not well and not for long.
To explain: If there are (at least) 8 clock ticks per bit, and one bit is a continuous pulse either high or low, it isn't hard for a patiently waiting receiver to sync when it arbitrarily receives the start bit of a transmission. Further, even with 8N1 there's two extra bits per byte which always follow the same pattern for this purpose (last bit high, first bit low). I've done this with an FPGA and so has this person; have a look at the diagram at the top there, which is much like this one from Wikipedia:
Those are the serial data bits, so again, there's a clock running at least 8 times as fast for timing, meaning all the receiver has to do is count. The furthest this count is going to be off is one tick, and that's not enough to misread an entire bit. For a mismatch in the clocks to problematize this, it would have to be whopping -- not like the equivalent of a second difference every few hours, but something closer to 10 seconds per minute. Unless they are damaged beyond any usability, as far as I'm aware crystal oscillator clocks used for hardware timing can't possibly go that far wrong.
So, rule out the clock skew.
If I terminate my Python script and re-run, it immediately works again. However, my application needs to eventually control a motor, so that kind of downtime is unacceptable.
Ideally the logic of the relationship should be such that the Arduino can be easily reset by the Pi in the event of some condition such as this. As in, via a connection between a GPIO on the Pi and the reset pin on the Arduino.
By "ideally the logic..." I mean something that is probably pretty simple, since if the Pi can't be sure of what's going on with the Arduino, then likely the only useful thing that can be done is reset it anyway. And a hard reset is bulletproof.
You don't have to restart the python program on the Pi, so it can maintain state, know what the Arduino is supposed to do next even if the Arduino doesn't, and that division of responsibility should be clear; whatever the Arduino is doing, it should be functioning as something with little or no need for long term accumulation of local data (especially since it doesn't have any potential for such) or long term independent changes of state, meaning having it stop and start again should't matter much (especially at the point where what it's actually doing in relation to the Pi has become ambiguous, perhaps very wrong, etc). Where it restarts from should, by definition, be a known state, and whatever states it's capable of should be ones it can then immediately be put into by the Pi.
Looking at your problem, if it were me, I'd start by blaming myself, i.e., assume I've done something wrong (likely on the Arduino side) that I've missed. The arbitrary reset is a ham fisted and not very satisfying solution, but:
If this is a long running system you have to have it anyway. Even if you are 100% positive you will never need to use it, you should have it anyway. Like a seatbelt. If it is too much trouble, then we might as well consider "doing things properly" in general too much trouble, in which case being 100% positive there could never be a need for a reset is overly optimistic. "It work good until it broken", as they say.
If you do need to make regular use of the reset, but not often enough that it's seriously impairing functionality, then it may allow you to keep going and working on things until you find your mistake (or even if already know where it is, but have some reason to defer correcting it until some other things are done).