Skip to content

avoid_bare_await_in_do

v1.0.0 Warning fpdart

This rule flags an await inside an asynchronous Do body whose operand is not an extraction through the block’s $ function.

Do tracks a block’s steps through its extraction function: $ is what makes a failing step short-circuit the rest of the block, and what turns a thrown error into a Left.

A bare await someFuture bypasses that machinery entirely. The future runs outside the block’s control, and when it fails the exception escapes as an ordinary exception — past every fold and match the caller wrote, because those only ever see the error channel.

This is one of four Do pitfalls that fpdart documents in its own do_constructor_pitfalls example.

See also: fpdart: Do notation

analytics.track is awaited directly, so when it rejects the exception escapes the pipeline as an ordinary exception — past the match the caller wrote:

TaskEither<String, String> checkout(
TaskEither<String, String> chargeCard,
Future<void> Function(String) track,
) => TaskEither.Do(($) async {
final receipt = await $(chargeCard);
await track(receipt); // escapes the Do tracking
return receipt;
});

Wrap the raw future so it becomes a step the block owns, with its failure in the left channel:

TaskEither<String, String> checkout(
TaskEither<String, String> chargeCard,
Future<void> Function(String) track,
) => TaskEither.Do(($) async {
final receipt = await $(chargeCard);
await $(TaskEither.tryCatch(() => track(receipt), (e, s) => '$e'));
return receipt;
});

When the side effect genuinely must not fail the pipeline, say so — wrap it and discard the outcome rather than leaving a bare await:

TaskEither<String, String> checkout(
TaskEither<String, String> chargeCard,
Future<void> Function(String) track,
) => TaskEither.Do(($) async {
final receipt = await $(chargeCard);
await $(
TaskEither.tryCatch(() => track(receipt), (e, s) => '$e').orElse(
(_) => TaskEither.of(null),
),
);
return receipt;
});

Only asynchronous blocks are checked. Option.Do, Either.Do and the IO* variants are synchronous and cannot hit this.

An await inside a closure declared within the body is not reported: that closure has its own async context, so the await was never one of the block’s tracked steps.

This rule is in the core preset, so it is on with preset: core, preset: recommended or preset: opinionated.

To turn it off:

many_lints.yaml
rules:
avoid_bare_await_in_do: false

To keep the rule on but skip certain paths, use per-rule exclude.