# Flutter: “Failed assertion: ‘!_debugLocked’: is not true” (Navigator)

> Failed assertion: !_debugLocked is not true. A second push or pop started while the first was still running. Make it one navigation, or defer the second.

Canonical: https://app369.com/fix/flutter-navigator-debuglocked-is-not-true/
Last updated: 2026-11-08

Navigating throws Failed assertion: line … pos …: ‘!_debugLocked’: is not true, from navigator.dart. The Navigator was already in the middle of a push, pop or page update, and code asked it to navigate again. Typical triggers are a pop inside a popUntil predicate, a dialog’s .then() that pops again while the dialog’s own pop is running, a pop from dispose() or a pop handler, a double tap that fires two pops, and a redirect from build().

**Short answer**

Run one navigation at a time. Replace a pop followed by a push with one call, such as pushReplacement or pushAndRemoveUntil; await a dialog before navigating again; and never navigate from dispose(), a popUntil predicate or build(). When the navigation reacts to state (a login result, a stream event), schedule it after the frame with WidgetsBinding.instance.addPostFrameCallback.

## **Why the Navigator is locked, and the fix**

1. 01 ### **A second navigation while the first is still running** A pop inside a popUntil predicate, a showDialog(...).then() that pops again while the dialog’s own pop is running, a pop from dispose() or a pop handler, a pop followed straight away by a push, or a double tap on a back button all start a second operation before the first has finished. Use the single method that does both, or await the first one.```
// instead of pop() then push()
Navigator.of(context).pushReplacement(
  MaterialPageRoute(builder: (_) => const ResultPage()),
);

// back to the first screen and open another
Navigator.of(context).pushAndRemoveUntil(
  MaterialPageRoute(builder: (_) => const HomePage()),
  (route) => false,
);

// a dialog: await it, then navigate once
final ok = await showDialog<bool>(
  context: context,
  builder: (_) => const ConfirmDialog(),
);
if (ok == true && context.mounted) Navigator.of(context).pop();
```
2. 02 ### **Navigator called from build()**
  > “Schedule a callback for the end of this frame.” [**Flutter, SchedulerBinding.addPostFrameCallback**](https://api.flutter.dev/flutter/scheduler/SchedulerBinding/addPostFrameCallback.html) Redirecting from build() (for example when a FutureBuilder or BlocBuilder sees the user is logged in) navigates in the middle of a frame, and fails when the Navigator itself is rebuilding. Do it after the frame instead, and only once.```
if (state.isLoggedIn && !_redirected) {
  _redirected = true;
  WidgetsBinding.instance.addPostFrameCallback((_) {
    if (!context.mounted) return;
    Navigator.of(context).pushReplacementNamed('/home');
  });
}
```
3. 03 ### **A listener navigates while the Navigator is popping**
  > “This method does not request a new frame.” [**Flutter, SchedulerBinding.addPostFrameCallback**](https://api.flutter.dev/flutter/scheduler/SchedulerBinding/addPostFrameCallback.html) A stream, ChangeNotifier or WillPopScope/PopScope handler that pushes while a pop is still running hits the same lock. Defer the navigation to after the frame. addPostFrameCallback does not request a frame, so when the event can arrive while the app is idle, call ensureVisualUpdate too.```
authStream.listen((user) {
  WidgetsBinding.instance.addPostFrameCallback((_) {
    navigatorKey.currentState?.pushNamedAndRemoveUntil('/login', (_) => false);
  });
  WidgetsBinding.instance.ensureVisualUpdate();
});
```

Checked against the vendor’s docs on November 8, 2026: [Flutter API: SchedulerBinding.addPostFrameCallback](https://api.flutter.dev/flutter/scheduler/SchedulerBinding/addPostFrameCallback.html).

## **Rather hand it over?**

Fixed scope, fixed price, paid upfront. The work happens in writing; no call needed.

- [from $450**App Fix Ticket**One reproducible bug in an existing mobile or web app, diagnosed and fixed.**See scope and start**](https://app369.com/fixed-price/flutter-firebase-bug-fix/)
- [from $450**AI-Built App Rescue**A Lovable, Bolt or Cursor app checked end to end in 3 days, then its worst problems fixed at a fixed price.****No issues worth fixing, no charge.**** If the Launch Check finds nothing rated medium or higher, Simon refunds the full $450. You keep the report either way. This applies to the first 10 Launch Checks bought after October 30, 2026.**See scope and start**](https://app369.com/fixed-price/ai-app-rescue/)

Related: [**Flutter “Navigator operation requested with a context”**](https://app369.com/fix/flutter-navigator-operation-requested-with-a-context/) · [**Flutter setState() called during build**](https://app369.com/fix/flutter-setstate-called-during-build/) · [**Flutter “deactivated widget’s ancestor”**](https://app369.com/fix/flutter-looking-up-a-deactivated-widgets-ancestor-is-unsafe/) · [**Flutter “type 'Null' is not a subtype of type 'String'”**](https://app369.com/fix/flutter-type-null-is-not-a-subtype-of-type-string/)

Questions

## **More on this error**

<details>

<summary>**Does Future.microtask or Future.delayed(Duration.zero) work too? **+</summary>

Often, because it moves the call out of the current build. addPostFrameCallback is clearer about when it runs, so prefer it for navigation that reacts to state.

</details>

<details>

<summary>**I use go_router. Can this still happen? **+</summary>

Yes, if context.go runs inside build. Use go_router’s redirect for auth-based routing instead of navigating from build.

</details>

<details>

<summary>**Can you fix it for me? **+</summary>

If one bug is the whole problem, the App Fix Ticket covers it: a written diagnosis first, then the fix. If the app has more going on, the Launch Check is a written check of the whole app. Each issue found is rated critical, high, medium or low, with where it is and how it was confirmed. No issues worth fixing, no charge. If the Launch Check finds nothing rated medium or higher, Simon refunds the full $450. You keep the report either way. This applies to the first 10 Launch Checks bought after October 30, 2026.

</details>

A different error?

## **Describe it and get a written quote**

Send the error and what you were doing. You get a written scope, a fixed price and a timeline; Simon replies by email, usually within one business day.

[**Describe your problem **→](https://app369.com/contact/) [**All fixes**](https://app369.com/fix/)