avoid-uncaught-future-errors
- Group
- Correctness
- Path
lint/correctness/avoid-uncaught-future-errors- Default
- On (recommended)
- Origin
- Falcon
- Rule
- 151 of 512
Flags local futures first awaited after another await inside a try/catch.
A future can complete with an unhandled error while execution is suspended, before the later await attaches the surrounding catch's error handler. Await it immediately or attach an error handler before the first async gap.
This conservative analysis follows straight-line statements directly in a
try body with at least one catch. It tracks resolved Future-returning calls
and constructors assigned to locals, and reports the later awaited local,
not discarded futures. Unknown base types and FutureOr are excluded. Early
catchError, onError, and then(onError:) calls are exempt, including handlers
attached in a separate statement or initializer. Parentheses are transparent.
Branches, loops, nested blocks, local declarations of functions, and other
expression statements end tracking: no control-flow or alias assumptions are
made across them. Late and multi-variable declarations are excluded. Nested
functions and shadowed locals cannot consume an outer candidate. Only root
await expressions in expression, return, and local-variable statements count
as gaps; awaits nested in other expressions are outside the matching limits.
A reference inside a composite await operand conservatively consumes that
candidate, covering combinators and forwarding methods without alias guesses.
A handler protects only the call it terminates, not subsequent chained calls.
When intermediate types are unavailable, only SDK Future method chains
(then/catchError/onError/timeout/whenComplete) on a proven Future base qualify.
Invalid
import 'dart:async';
Future<int> start() async => 1;
Future<void> gap() async {}
Future<void> inferredLocal() async {
try {
final pending = start();
await gap();
await pending;
} catch (_) {}
}
Future<void> explicitLocal() async {
try {
Future<int> pending = start();
await gap();
await pending;
} catch (_) {}
}
Future<void> parenthesized() async {
try {
final pending = (start());
await (gap());
await (pending);
} catch (_) {}
}
Future<void> initializerGap() async {
try {
final pending = start();
final result = await start();
await pending;
} catch (_) {}
}
Future<int> returnAwait() async {
try {
final pending = start();
await gap();
return await pending;
} catch (_) {
return 0;
}
}
Future<void> multiplePending() async {
try {
final first = start();
final second = start();
await gap();
await first;
await second;
} catch (_) {}
}
Future<void> lateHandler() async {
try {
final pending = start();
await gap();
pending.catchError((Object error) => 0);
await pending;
} catch (_) {}
}
Future<void> laterChainCanFail() async {
try {
final pending = start().catchError((Object error) => 0).timeout(const Duration(seconds: 1));
await gap();
await pending;
} catch (_) {}
}
Future<int> withRetry(Future<int> pending) => pending;
Future<void> uncalledClosureCannotConsume() async {
try {
final pending = start();
final retry = () async => await withRetry(pending);
await gap();
await pending;
} catch (_) {}
} await gap(); await pending; await gap(); await pending; await (gap()); await (pending); final result = await start(); await pending; await gap(); return await pending; await gap(); await first; await first; await second; pending.catchError((Object error) => 0); await pending; await gap(); await pending; await gap(); await pending;Valid
import 'dart:async';
Future<int> start() async => 1;
Future<void> gap() async {}
Future<void> immediatelyAwaited() async {
try {
final pending = start();
await pending;
await gap();
await pending;
} catch (_) {}
}
Future<void> initializerHandler() async {
try {
final pending = start().catchError((Object error) => 0);
await gap();
await pending;
} catch (_) {}
}
Future<void> typedHandler() async {
try {
final pending = start().onError<Exception>((error, stack) => 0);
await gap();
await pending;
} catch (_) {}
}
Future<void> thenHandler() async {
try {
final pending = start().then((value) => value, onError: (Object error) => 0);
await gap();
await pending;
} catch (_) {}
}
Future<void> separateHandler() async {
try {
final pending = start();
pending.catchError((Object error) => 0);
await gap();
await pending;
} catch (_) {}
}
Future<void> handlerInitializer() async {
try {
final pending = start();
final handled = pending.catchError((Object error) => 0);
await gap();
await pending;
await handled;
} catch (_) {}
}
Future<void> dynamicType(dynamic Function() create) async {
try {
final pending = create();
await gap();
await pending;
} catch (_) {}
}
Future<void> noCatch() async {
final pending = start();
await gap();
await pending;
}
Future<void> finallyOnly() async {
try {
final pending = start();
await gap();
await pending;
} finally {}
}
Future<void> discarded() async {
try {
final pending = start();
await gap();
} catch (_) {}
}
Future<void> nestedFunction() async {
try {
final pending = start();
Future<void> later() async {
await gap();
await pending;
}
await pending;
} catch (_) {}
}
Future<void> shadowed() async {
try {
final pending = start();
{
final pending = start();
await pending;
}
await pending;
} catch (_) {}
}
Future<void> reassigned() async {
try {
var pending = start();
await gap();
pending = start();
await pending;
} catch (_) {}
}
Future<void> aliased() async {
try {
final pending = start();
final alias = pending;
alias.catchError((Object error) => 0);
await gap();
await pending;
} catch (_) {}
}
Future<void> lazyInitialization() async {
try {
late final pending = start();
await gap();
await pending;
} catch (_) {}
}
Future<void> multipleDeclarators() async {
try {
Future<int> first = start(), second = start();
await gap();
await first;
await second;
} catch (_) {}
}
Future<void> aggregateAwait() async {
try {
final first = start();
final second = start();
await Future.wait([first, second]);
await first;
await second;
} catch (_) {}
}
Future<void> forwardingAwait() async {
try {
final pending = start();
await pending.timeout(const Duration(seconds: 1));
await pending;
} catch (_) {}
}
Future<void> compositeHandlerStatement() async {
try {
final pending = start();
pending.then((value) => value).catchError((Object error) => 0);
await gap();
await pending;
} catch (_) {}
}
Future<void> compositeHandlerInitializer() async {
try {
final pending = start();
final handled = pending.then((value) => value).catchError((Object error) => 0);
await gap();
await pending;
await handled;
} catch (_) {}
}
FutureOr<int> maybeStart() => 1;
Future<void> futureOrIsNotProof() async {
try {
final pending = maybeStart();
await gap();
await pending;
} catch (_) {}
}
Future<void> conditionalGap(bool condition) async {
try {
final pending = start();
if (condition) {
await gap();
}
await pending;
} catch (_) {}
}
Future<void> loopBoundary(List<int> values) async {
try {
final pending = start();
for (final value in values) {
await gap();
}
await pending;
} catch (_) {}
}Configure avoid-uncaught-future-errors
Set its severity in falcon.json. See the configuration guide.
{
"linter": {
"rules": {
"correctness": {
"avoid-uncaught-future-errors": "error"
}
}
}
}Or silence one occurrence with a suppression comment; the reason is required.
// falcon-ignore lint/correctness/avoid-uncaught-future-errors: <reason>More correctness rules
- avoid-unconditional-break
Reports unconditional break, continue, return or throw in a for-loop body.
- avoid-undisposed-instancesrecommendedflutter
Warns when a disposable constructor result is discarded.
- avoid-unmodified-loop-conditionrecommended
Flags loop conditions whose local scalar inputs are never modified by the loop.
- avoid-unnecessary-setstaterecommendedflutter
Avoid setState during build, initState, or didUpdateWidget.
- avoid-unnecessary-stateful-widgetsrecommendedflutter
Warns when a
StatefulWidget's local State contains only a pure build method. - avoid-unreachable-for-loop
Reports iteration of a collection in a branch that requires it to be empty.
- avoid-unrelated-type-castsrecommended
Flags casts between types proven unrelated by canonical type resolution.
- avoid-unsafe-collection-methods
Flags potentially throwing element access on SDK collections.