async-safety

Enforces no-silent-failure async discipline in Flutter and Dart code.

Updated Aug 22, 2026
One-click install
npx skills add https://github.com/zakariaf/NearlyStop --skill async-safety-zakariaf
Or copy as Structured Prompt for Agent▼
Please help me install this Agent Skill.
Skill: async-safety
Source: https://github.com/zakariaf/NearlyStop/tree/main/.claude/skills/async-safety
Command: npx skills add https://github.com/zakariaf/NearlyStop --skill async-safety-zakariaf

SYSTEM DOCUMENTATION & REQUIREMENTS

What problem does it solve? Async bugs in Flutter are silent: a dropped Future swallows its error, a dead BuildContext throws into a void, and a leaked subscription fires into a disposed widget. The arrow-callback pattern onTap: () => vm.save(x) passes every lint while discarding both the Future and its error, and no analyzer rule catches it. ## Core Features & Use Cases - Callback Future-drop prevention: Requires every onTap/onPressed/VoidCallback handler to return void, routing async work through a void method that wraps the call in unawaited(... .catchError(...)). - Async gap guards: Mandates a mounted/ref.mounted check after every await before touching BuildContext, or capturing Navigator/ScaffoldMessenger before the gap, with the guard placed after the last of multiple awaits. - Resource teardown and honest catches: Requires disposing every StreamSubscription, StreamController, Timer, and sink, typed catches that log stack traces and surface failures, rethrow over throw e, and no asserts on plugin return values. - Use Case: When reviewing a Flutter pull request that wires a save button, apply this Skill to rewrite onTap: () => vm.save(note) into a void handler with a catchError chain, add mounted guards after awaits, and verify dispose() cancels all subscriptions. ## Quick Start Review this Flutter widget's async code and fix any silent-failure patterns such as arrow callbacks returning Futures, missing mounted guards after awaits, or undisposed subscriptions.

Frequently Asked Questions about async-safety

High-intent search queries and answers about installing and using this skill.

FAQPage Schema
How do I fix the onTap arrow callback dropping a Future in Flutter?▼

Route the tap through a void-returning handler method instead of `onTap: () => vm.save(x)`. Inside that method, call `unawaited(_persist(x).catchError(...))` so the error is logged and surfaced rather than silently discarded.

How do I use BuildContext after an await in Flutter?▼

Either capture Navigator.of(context) or ScaffoldMessenger before the await, or place `if (!mounted) return;` immediately after the await and before the context use. With multiple awaits, the guard must sit after the last one.

Why does discarded_futures not catch my async onTap callback?▼

The arrow closure `() => vm.save(x)` returns the Future, so discarded_futures, unawaited_futures, and unused_result all consider it used, while the VoidCallback target type discards it. This shape is caught by no lint and must be fixed structurally with a void handler.

Does Riverpod need a mounted check after await?▼

Yes. In a Riverpod 3.x Notifier or AsyncNotifier, use `if (!ref.mounted) return;` after an await before assigning state, since the provider may have been disposed during the suspension.

When is unawaited() acceptable in Dart?▼

unawaited is acceptable only when the Future provably cannot fail silently, meaning it ends in a .catchError(...) handler or is total. Bare unawaited on a fallible path routes errors to PlatformDispatcher.onError, detached from the UI.

Why should I avoid assert on plugin return values in Flutter?▼

Asserts are stripped in release builds, so a check that passes in every test disappears on the device, creating a silent failure. Handle plugin results with explicit runtime checks and typed error handling instead.