Oh, I think I’ve found the cause
The response will contain a new access token and refresh token. You must save both tokens in order to maintain api access.
If, for whatever reason, your app doesn’t receive the response, or fails to save the new token, you can retry using your existing refresh token for a grace period of 30 minutes. After 30 minutes your previous refresh token will expire and the user will need to re-authorise your api application in order to generate a new refresh token.
It seems that Xero invalidates refresh token during refresh sequence and returns a new refresh_token alongside with access_token. Unfortunately, we do not support this scenario with generic WebLocation connector - we treat refresh_token, once received during interactive authorization, as immutable.
In the latest (v5) release we’ve added mutable state / shared memory system to handle these cases, but still not in generic way - every provider seems to invalidate refresh tokens in their own fashion.