Part of this was a known issue with LND (your intuition was spot-on nostr:npub186tqtnx8nc502veqkguypvzpm5hq53p69qrl8l0hes9c4k8mzrssfk59su)
If you want to see which inflight payments are actually stuck you can do something like this:
lncli listpayments --include_incomplete |
jq -r '
.payments |
.[]
| select(.status == "IN_FLIGHT") |
[.payment_hash, .value_msat, ([.htlcs | .[] | select(.status == "IN_FLIGHT") | .route.total_time_lock ] | max)] |
@tsv
'
Then check that the max route.total_time_lock is in the future