Critical Betaflight GPS Bug That Can Freeze Your FC Mid-Flight
A Betaflight GPS-related bug that can cause a crash mid-air, and how to fix it
Intro
This article covers a pretty obscure but critical Betaflight bug related to how the Flight Controller communicates with the GPS. If configured incorrectly, it can cause the FC to freeze mid-air and remain locked until power-cycled.
Affected setups
At the time of writing, this issue has been observed on Betaflight 4.4 and newer, with GPS enabled on a configured UART where communication fails during initialization.
Tested hardware:
- FCs: Reliably reproduced on F7 MCUs, potentially affects other MCU types as well
- GPS: Observed across multiple GPS models
- Protocol: Observed on UBLOX, potentially affects NMEA as well
Issue uncovered
The issue is triggered when Betaflight expects GPS communication on a UART but cannot initialize the GPS correctly, where a disconnected, missing, or misconfigured GPS can completely freeze the FC in-flight, and it doesn’t recover until unplugging the battery completely.
The issue can occur when ALL of the following are true:
- GPS is enabled in the Ports tab
- Betaflight expects a GPS on that UART
- The GPS is disconnected, powered incorrectly, wired to the wrong UART, or using the wrong baud rate
- Betaflight cannot initialize communication with the GPS
Look at the image above - GPS is enabled on UART2. In itself is not an issue, but if the GPS is not actually connected to UART2, it can trigger the issue.
Warning
It is important to note that this is not related to GPS Rescue or Failsafe modes, rather the Ports tab specifically.
It does not matter if GPS Rescue is configured or not, as long as GPS is active in the Ports tab and Betaflight isn’t communicating with it properly, this issue can happen.
If you have a GPS installed, the issue can still happen if the connection between the FC and GPS is interrupted. When this happens, all GPS-related OSD elements will disappear.
Info
This issue is already reported on the Betaflight’s GitHub Issue Tracker, track here
If you had this issue happen, please report there! Include your full CLI dump and blackbox logs (if possible).
Example of the issue
Unfortunately, I’ve had this happen multiple times before I figured out what was causing this.
It can be easily identified:
- The drone randomly falls out of the sky
- Looks similar to a motor stall/desync
- BUT the OSD freezes (and on DJI, disappears shortly after)
- Drone doesn’t arm or react to any commands from the TX, although still bound
Here is a video example of the issue happening in-flight:
Notice how the OSD freezes once the drone (and the FC) stalls, then shortly disappears.
Info
Even when the trigger conditions are present, the crash may not occur on every flight.
So if you’re having intermittent issues that look just like the examples above, but they’re not consistent, it still absolutely might be this issue at play!
How to prevent it
Preventing this issue is simple:
- Always make sure that GPS is enabled in Ports tab ONLY if it is actually attached
- Make sure it is on the correct UART (Ports tab matches the wiring)
AND if your drone has a GPS, add this simple check to your pre-flight checklist:
- Verify that the GPS-related OSD elements (like satellite count & distance from home) are present in the OSD prior to take-off
Danger
If GPS-related OSD elements are completely missing before takeoff (when expected), do NOT arm.
If the elements are visible but showing 0 satellites, that is fine.
Missing elements entirely indicates the GPS has not initialized correctly.
This only applies if you normally have GPS OSD elements configured and visible.
Why is it critical?
From testing, the issue appears to fully stall the FC rather than trigger a normal failsafe condition. RX link may remain active, but the FC stops responding entirely until rebooted.
So not only the drone falls out of the sky, but it also cannot be re-armed or even controlled at all until unplugging the battery.
Recovery may become impossible if the quad crashes into water, tall trees, or inaccessible terrain, since turtle mode and the beeper will no longer function.
Should you worry about it?
If your GPS is wired correctly and functioning normally, this issue is unlikely to affect you.
The important part is simply knowing how to identify the warning signs before takeoff:
- Missing GPS OSD elements
- Incorrect UART configuration
- GPS communication failures
A quick pre-flight OSD check is usually enough to avoid the issue entirely.
The technical details
Note
This section is for the curious. You don’t need any of it to stay safe - the prevention steps above are enough on their own.
What Betaflight is actually doing
When you assign a UART to GPS, Betaflight doesn’t know what baud rate your module is set to, so it goes hunting for it.
It sends the GPS a short “who are you?” request, waits about a third of a second, repeats that three times, then switches the UART to the next baud rate in its list and tries again. There are six baud rates in that list, so one full sweep takes roughly six seconds.
The only way out of that loop is the GPS answering.
If a GPS answers, the whole dance is over in about a second and never runs again for the rest of the flight. If nothing answers, it never stops. It keeps stepping through baud rates for as long as the battery is plugged in.
The part that actually matters
Changing the baud rate is not a small operation. To do it, Betaflight shuts the UART hardware down completely and rebuilds it from scratch, every single time.
That rebuild has never been entirely safe to perform while the port is live, and Betaflight’s developers know it - there’s a note left in the firmware, sitting directly above that routine, warning that it doesn’t clean up after itself properly.
For developers: the exact call path
Betaflight calls serialSetBaudRate(), which calls uartReconfigure(). That routine fully tears down the USART peripheral along with both of its DMA streams, then rebuilds them from scratch.
The warning sitting directly above it in the source reads, verbatim:
// XXX uartReconfigure does not handle resource management properly.The GPS half lives in gps.c, in the GPS_STATE_DETECT_BAUD case. Its only exit is a successfully parsed MON-VER reply from the module, so with nothing on the other end it loops forever.
So the picture looks like this:
| GPS status | UART torn down and rebuilt |
|---|---|
| Connected and working | about twice, at boot, then never |
| Configured but not talking | about once a second, forever - roughly 3600 times an hour |
That is the entire difference. It isn’t the GPS parsing, it isn’t GPS Rescue, and it isn’t the failsafe logic. It is one operation that was never fully safe, repeated a few thousand times per flight instead of twice.
Why that freezes the flight controller
Rebuilding a UART while it’s still live is a gamble. Serial ports constantly interrupt the processor to hand over each new byte as it arrives - and if one of those interrupts lands during the split second when the port is being rebuilt, the flight controller can get stuck answering that same interrupt over and over, forever.
The processor is still running flat out. It just never gets back to the job of flying the quad. Motors stop receiving updates, the OSD freezes on its last frame, and the flight controller stops responding to anything at all. Betaflight has no watchdog to notice this and reset itself, so it stays that way until you unplug the battery.
This isn’t a theory about what could happen. Betaflight has already found and fixed three separate bugs of exactly this shape in the same piece of code. One of those fixes describes this very scenario:
Fixes a UART interrupt-storm freeze that occurs when a UART is configured for GPS but no module is connected: after several hours the flight controller becomes unresponsive (OSD stops updating, control is lost, only a power cycle recovers). Reproduced by reporter on CHAMP_H743, HDZero HALO FC, Matek H743 FC, and SpeedyBee F7 V3.
It names the trigger directly, too - the GPS baud-rate hunt, rebuilding the port over and over again.
Why it’s random
Two things have to line up at the same instant:
- The scan has to be running. Guaranteed, permanently, whenever the GPS isn’t talking.
- A serial interrupt has to land inside a window a few CPU instructions wide, right in the middle of the rebuild.
The first is constant. The second is a coin flip, weighted by how much serial interrupt activity is happening at that moment. That’s why it’s roughly 1 flight in 10 rather than every flight, and why a flight controller can sit on the bench for hours without a single freeze.
It also explains why it’s worse in the air. An unconnected GPS pin is left floating with no pull resistor, so it picks up electrical noise from the ESCs, and every noise glitch that vaguely resembles a byte is another interrupt. Blackbox logging to flash also stalls the CPU in bursts while serial data keeps arriving. Both load the dice, and both only really happen while you’re flying.
Info
To be honest about the limits here: I know both of those effects are real and both point the same way, but I haven’t measured which one dominates. That part is reasoning, not a measurement.
Why F7 and H7 are hit hardest
The chip manufacturer supplies two different sets of low-level serial code, and Betaflight uses a different one depending on which processor your board has.
F4 boards (F405, F411) get the older, stricter one. Before it reacts to anything the serial port reports, it double-checks that the port was actually asked to report it in the first place. Leftover state gets ignored.
F7, H7 and G4 boards get the newer one, which skips that second check. It will happily act on leftover state that nothing asked for - and that is exactly how the freeze starts.
Two of the three known freeze paths simply don’t exist on F4 as a result. It lines up with the reports, too: almost all of them are F722 and H743 boards.
Warning
F4 is safer here, not immune. It has its own version of the risky teardown window, just with better protection around it.
For developers: which libraries, and the actual difference
F4 uses ST’s Standard Peripheral Library. Its interrupt handler tests with USART_GetITStatus(), which checks the interrupt-enable bit and the flag.
F7, H7 and G4 used ST’s HAL, whose __HAL_UART_GET_IT() reads only the flag, so it can act on state that nothing armed. Betaflight migrated those chips to ST’s LL (Low Layer) API in 2026, which does check both.
F4 also never calls HAL_UART_DeInit() or HAL_DMA_Init(), and that is where the other two freeze paths live. The second of those is a literal while (1); that deadlocks the processor outright if a DMA stream fails to report itself shut down in time.
How far back this goes
I traced it back through Betaflight’s version history. The bug needs two ingredients: the GPS scan that never gives up, and the UART driver that isn’t safe to rebuild. The GPS scan has been there essentially forever. The driver arrived with F7 support.
| Version | When | Affected? |
|---|---|---|
| v3.0.x and older | up to Oct 2016 | No - F7 support didn’t exist yet |
| v3.1.0 | Jan 2017 | Yes - the first affected release |
| v3.2.0 through 4.3.x | 2017 - 2022 | Yes |
| 4.4.x | 2023 - 2024 | Yes - first reports appear here |
| 4.5.x | 2024 - 2025 | Yes - most reports, including mine |
| 2025.12.0 - 2025.12.2 | 2026 | Yes |
| 2025.12.3 and later | 2026 | Mostly fixed |
| 2026.6 and current dev | 2026 | Partly fixed |
So this has been sitting in Betaflight since January 2017 - about nine and a half years. It only started getting noticed around 4.4 because that’s when GPS became common on ordinary builds, and when F7 and H7 boards took over from F4.
Where the fixes actually are
This is the part I’d most want people to know, because it’s counter-intuitive.
Two driver-level fixes were written. Both of them landed on the 2025.12 maintenance branch. Only one of them was carried forward to the current development version. The result is that 2025.12.3+ is currently better protected against this than the newer releases are.
And more importantly:
Danger
The underlying cause has never been fixed in any version.
Every release - including the newest - still rebuilds the UART about once a second, forever, whenever a GPS is configured but not talking. Only the downstream crash paths have been patched, and only on some branches.
That’s why the advice in this article still stands regardless of which version you run: if there’s no GPS fitted, don’t leave the port assigned to one.
For developers: watching the loop on your own quad
Set debug_mode = GPS_CONNECTION in the CLI, then with a GPS port configured and nothing connected to it:
- debug[4] sits at 100, which is
GPS_STATE_DETECT_BAUDencoded as state x 100 + step - debug[2] climbs continuously, counting every baud step and therefore every UART rebuild
- debug[7] shows bytes waiting in the receive buffer, a rough proxy for how much interrupt activity that floating pin is generating
If debug[4] is parked at 100 and debug[2] keeps climbing while you fly, the trigger is live on your aircraft.