Паника распространяется из Do к вызывающему. Once считает, что f не выполнилась успешно - следующий вызов Do снова вызовет f. Чтобы считать "однократное выполнение" даже при панике, нужно recover внутри f и как-то сохранить результат (например, в переменную).