The TIMx_ARR may be preloaded, by setting TIMx_CR1.ARPE bit. This means, that writes to this register don't change the timer's behaviour immediately, but will get active only after the next Update event, usually the next timer overflow.
If TIMx_ARR is set to 0, timer will stop at the next update. Alternatively, if this is set while timer is not running, then after enabling timer it won't run.
If this is combined with preload being set, there will never be an Update due to overflow, so setting TIMx_ARR to a non-zero value won't get "active" and the timer won't run (unless Update is subsequently forced by setting TIMx_EGR.UG, or from the slave-mode controller set to Trigger mode).
While this may seen as obvious, the TIM initialization may be performed by a library such as Cube or SPL, and the user may not be aware of the TIMx_CR1.ARPE being set, or the consequences of this.