withdrawFees can fail, because the equation address(this).balance == uint256(totalFees) is too strict.
If the account hasn't exactly the same ether as totalFees, withdraw is impossible, This situation is very likely, if rounding will happen during calculation of the totalFees and prizePool.
Let's break down the numbers:
If there are 9 players and each pays an entrance fee of 1,000,000,000,000,001 wei:
The total fees collected will be 20% of the entrance fees:
1,000,000,000,000,001 (entrance fee) x 9 (players) x 0.2 = 1,800,000,000,000,001.8.
Solidity rounds this down to 1,800,000,000,000,001.
The prize pool will be 80% of the entrance fees:
1,000,000,000,000,001 (entrance fee) x 9 (players) x 0.8 = 7,200,000,000,000,007.2.
This gets rounded down to 7,200,000,000,000,007.
Before distributing the prize, our account holds:
1,000,000,000,000,001 x 9 = 9,000,000,000,000,009 wei.
After giving out the prize, our account will have:
9,000,000,000,000,009 - 7,200,000,000,000,007 = 1,800,000,000,000,002 wei left.
However, notice that the calculated total fees are 1,800,000,000,000,001, which is 1 wei less than the difference above.
withdrawFee is never permitted, i.e. funds can never be withdrawn.
Math
require(address(this).balance == uint256(totalFees), "PuppyRaffle: There are currently players active!"); is used only to validate that the game is finished and selectWinner has been called.
After each successful selectWinner the players array is reset with delete, which will reset the array length to zero.
It would be ok, to replace the above validation with this:
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.