The thing to remember with the Leonardo and other ATMega32U4 based boards is that there is no actual serial connection.
What there is, is a USB connection which is controlled by the sketch (and independently by the bootloader) to provide a "CDC/ACM" connection over USB. This emulates a serial connection, but it's not a real serial connection.
This actually has a huge number of benefits, because you can be other things besides a serial port, such as a keyboard or a mouse. However, it comes at a cost: the serial port is at the mercy of the sketch that's running at the time.
If your sketch crashes for any reason then the serial port can no longer be used. That's bad. It can cripple your Arduino. Since it's the sketch that is providing the driving force for the CDC/ACM serial emulation, if that sketch dies, so does the control of the USB and thus the CDC/ACM.
So while the port may be created initially by the sketch, if it then subsequently crashes the port is still there, but unresponsive. Or the port can vanish completely.
So how do you fix it? Well, there's a couple of strategies:
- Some people have had success by pressing the RESET button twice in quick succession. This depends on the version of the bootloader in use though, and it's not something I have ever tried.
- Pressing RESET at just the right moment to get the bootloader to run just before
avrdude runs and catch it at the right time to accept a new sketch, or
- Using a hardware programmer (e.g., USBASP) or another Arduino to wipe the chip and reinstall the bootloader
My preferred method is 3, since it is certain to always work - however it requires either a hardware programmer or another Arduino. However you can pick up a USBASP on eBay / AliExpress / Amazon etc for next to nothing, and when you're working with AVR chips they're always good to have to get you out of tight spots like this.