Agent skill

Flutter Async Test Unhandled Future Rejection

by divinevideo in divinevideo/divine-mobile

Fix flaky Flutter/Dart tests that fail in CI but pass locally due to unhandled Future rejections.

MPL-2.0Auto-check passedMobile

Install Flutter Async Test Unhandled Future Rejection

skills CLI
$ npx skills add divinevideo/divine-mobile --skill flutter-async-test-unhandled-future-rejection -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install divinevideo/divine-mobile flutter-async-test-unhandled-future-rejection --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/divinevideo/divine-mobile.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.agents/skills/flutter-async-test-unhandled-future-rejection .claude/skills/flutter-async-test-unhandled-future-rejection && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
flutter-async-test-unhandled-future-rejection
GitHub stars
266
Token cost
~1.6k tokens
SKILL.md length
331 words
Files
1
Skills in repo
103
Repo updated
First seen
Licence
MPL-2.0

At a glance

Fix flaky Flutter/Dart tests that fail in CI but pass locally due to unhandled Future rejections.

  • Works in 3 steps: Test passes locally: flutter test… → Test passes in CI (check GitHub Actions… → Test is deterministic - run 10x with…
  • Test passes locally but fails in CI with cryptic errors
  • SKILL.md covers Problem, Context / Trigger Conditions, Solution and Verification, plus 4 more sections
  • Calls flutter

What it does

Flutter Async Test Unhandled Future Rejection is an agent skill from divinevideo/divine-mobile. Fix flaky Flutter/Dart tests that fail in CI but pass locally due to unhandled Future rejections. Use when: (1) Test passes locally but fails in CI with cryptic errors, (2) Test creates Futures that will throw errors (e.g., network calls to fake URLs), (3) Even with .catchError() at the end, test still fails, (4) Error message shows test name but truncated error like "ROR]" or "[ERROR]". Solution: Avoid creating Futures that will reject; test state machine behavior with synchronous operations instead.

Its SKILL.md is about 1.6k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

It sits in Mobile, covering Cross-platform mobile apps. It works with Flutter and Dart. The licence is MPL-2.0.

When your agent uses it

  • Test passes locally but fails in CI with cryptic errors
  • Test creates Futures that will throw errors (e.g.
  • Network calls to fake URLs)
  • Even with .catchError() at the end

Example prompts

  • “[ERROR]”
  • “/flutter-async-test-unhandled-future-rejection”

Workflow steps

3 steps, taken from the first numbered list in SKILL.md.

  1. Test passes locally: flutter test path/to/test.dart
  2. Test passes in CI (check GitHub Actions / other CI)
  3. Test is deterministic - run 10x with --repeat=10 flag

What it can do on your machine

Read from SKILL.md and the folder at commit 4c622be. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Shell commands in SKILL.md call:

    • flutter

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    Links to these hosts (documentation or services it may open):

    • dart.dev
    • api.flutter.dev

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Flutter Async Test Unhandled Future Rejection loads about 1.6k tokens when it runs. Until then it costs about 138 tokens; SKILL.md has 331 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~138
When it runs · the whole SKILL.md, loaded when a task matches
~1.6k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from divinevideo/divine-mobile at commit 4c622be, republished under its MPL-2.0 licence (© divinevideo). 331 words, ~1,596 tokens.

Download SKILL.mdSave it as .claude/skills/flutter-async-test-unhandled-future-rejection/SKILL.md (or your agent's skills folder).
name
flutter-async-test-unhandled-future-rejection
description
Fix flaky Flutter/Dart tests that fail in CI but pass locally due to unhandled Future rejections. Use when: (1) Test passes locally but fails in CI with cryptic errors, (2) Test creates Futures that will throw errors (e.g., network calls to fake URLs), (3) Even with .catchError() at the end, test still fails, (4) Error message shows test name but truncated error like "ROR]" or "[ERROR]". Solution: Avoid creating Futures that will reject; test state machine behavior with synchronous operations instead.
author
Claude Code
version
1.0.0
date
2026-02-01

Flutter Async Test Unhandled Future Rejection

Problem

Tests that create Futures which will reject (throw errors) can fail in CI even when:

  • You catch the error with .catchError() at the end
  • You use try/catch around the await
  • The test passes locally

The Flutter/Dart test framework detects "unhandled" Future rejections during test execution, even if you plan to handle them later. This causes flaky tests that pass locally but fail in CI due to timing differences.

Context / Trigger Conditions

Symptoms:

  • Test passes locally with flutter test but fails in CI
  • Error message is truncated or cryptic (e.g., "ROR]" instead of "[ERROR]")
  • Test name appears in error output but no clear assertion failure
  • Test involves creating Futures to URLs/resources that don't exist
  • Using patterns like:
    dart
    final future = someAsyncOperation(); // This will throw
    // ... do assertions ...
    await future.catchError((_) {}); // Too late - already flagged as unhandled

Common scenarios:

  • Testing that a method can only be called once (state guards)
  • Testing timeout/cancellation behavior
  • Testing error handling paths
  • Any test that intentionally triggers errors in async code

Solution

Don't create Futures that will reject - test the state machine directly

Instead of:

dart
test('start throws if already started', () async {
  final session = SomeSession(url: 'wss://fake.url');

  // BAD: This Future will reject when connection fails
  final startFuture = session.start();

  // Even this won't help - rejection already detected
  await Future.delayed(Duration.zero);

  expect(() => session.start(), throwsA(isA<StateError>()));

  // Too late to catch - test already failed
  await startFuture.catchError((_) {});
});

Do this:

dart
test('start throws if already started', () {
  // GOOD: Completely synchronous, no network calls
  final session = SomeSession(url: 'wss://example.com');

  // Use a synchronous state transition to exit the "startable" state
  session.cancel(); // Transitions state without network call

  // Now test that start() throws when not in initial state
  expect(
    () => session.start(),
    throwsA(isA<StateError>()),
  );

  session.dispose();
});
Alternative approaches if you must use async

Option 1: Wrap the Future creation in a zone that ignores errors

dart
test('handles async error', () async {
  late Future<void> errorFuture;

  await runZonedGuarded(() async {
    errorFuture = operationThatWillFail();
    // Do synchronous assertions here
  }, (error, stack) {
    // Ignore expected errors
  });
});

Option 2: Use expectLater for Futures that should fail

dart
test('operation fails with specific error', () async {
  // Let the test framework know this Future SHOULD fail
  await expectLater(
    operationThatWillFail(),
    throwsA(isA<SomeError>()),
  );
});

Option 3: Mock the async dependency

dart
test('start throws if already started', () async {
  final mockRelay = MockRelay();
  when(mockRelay.connect()).thenAnswer((_) async => {}); // Never fails

  final session = SomeSession(relay: mockRelay);
  await session.start();

  expect(() => session.start(), throwsA(isA<StateError>()));
});

Verification

  1. Test passes locally: flutter test path/to/test.dart
  2. Test passes in CI (check GitHub Actions / other CI)
  3. Test is deterministic - run 10x with --repeat=10 flag

Example

Before (flaky):
dart
test('NostrConnectSession start throws if already started', () async {
  final session = NostrConnectSession(relays: ['wss://relay.example.com']);

  // This creates a Future that will reject when relay connection fails
  final startFuture = session.start();

  // State changed synchronously, but Future rejection is pending
  expect(session.state, isNot(equals(NostrConnectState.idle)));
  expect(() => session.start(), throwsA(isA<StateError>()));

  session.cancel();
  session.dispose();

  // This doesn't help - rejection already flagged by test framework
  await startFuture.catchError((_) {});
});
After (reliable):
dart
test('NostrConnectSession start throws if already started', () {
  // Completely synchronous - no Futures that can reject
  final session = NostrConnectSession(relays: ['wss://relay.example.com']);

  expect(session.state, equals(NostrConnectState.idle));

  // Use cancel() to transition out of idle state synchronously
  session.cancel();
  expect(session.state, equals(NostrConnectState.cancelled));

  // Now start() throws because we're not in idle state
  expect(
    () => session.start(),
    throwsA(
      isA<StateError>().having(
        (e) => e.message,
        'message',
        contains('already started'),
      ),
    ),
  );

  session.dispose();
});

Notes

  • This issue is more common in CI because of different timing characteristics
  • The truncated error messages (like "ROR]") happen because CI output gets cut off
  • Local tests may pass because the garbage collector hasn't run yet
  • This is different from the "A Timer is still pending" error (see flutter-dispose-timer-test-failure skill)
  • When testing state machines, prefer testing state transitions over testing async behavior
  • If you need to test actual async/network behavior, use proper mocking
  • flutter-dispose-timer-test-failure: For timer-related test failures
  • riverpod-ref-in-provider-lifecycle: For async callback issues in Riverpod providers

References

© divinevideo, MPL-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in .agents/skills/flutter-async-test-unhandled-future-rejection of divinevideo/divine-mobile.

Open the folder on GitHubat commit 4c622be

Compare with similar skills

Flutter Async Test Unhandled Future Rejection next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Flutter Async Test Unhandled Future Rejection compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Flutter Async Test Unhandled Future Rejection this skilldivinevideo/divine-mobile266—~1.6kAutomated safety check: PassMPL-2.0
Engine Whats Newflutter/flutter179k—~978Automated safety check: PassBSD-3-Clause
Flutter Cherry Pickflutter/flutter179k—~1.8kAutomated safety check: PassBSD-3-Clause
Upgrade Browserflutter/flutter179k—~1.1kAutomated safety check: PassBSD-3-Clause
Bump Dartflutter/flutter179k—~1.2kAutomated safety check: PassBSD-3-Clause
Run Devicelab With Ledflutter/flutter179k—~786Automated safety check: PassBSD-3-Clause

Similar skills

  • Engine Whats New

    flutter/flutter

    Generates the "what's new" release summary and diff file for changes in the Flutter engine (//engine/src/flutter) between two releases (e.g., 3.47 vs 3.44).

    179k GitHub stars~978 tokensUpdated today
    MobileAuto-check passed
  • Flutter Cherry Pick

    flutter/flutter

    How to land a formal cherry-pick of a merged PR for the flutter/flutter repo stable or beta channel.

    179k GitHub stars~1.8k tokensUpdated today
    MobileAuto-check passed
  • Upgrade Browser

    flutter/flutter

    Upgrade browser versions (Chrome or Firefox) in the Flutter Web Engine and/or Framework tests.

    179k GitHub stars~1.1k tokensUpdated today
    MobileAuto-check passed
  • Bump Dart

    flutter/flutter

    Do not trigger automatically; only run when a user runs /bump-dart.

    179k GitHub stars~1.2k tokensUpdated today
    MobileAuto-check passed
  • Run Devicelab With Led

    flutter/flutter

    Run DeviceLab tests for Flutter PRs using LUCI's led CLI tool.

    179k GitHub stars~786 tokensUpdated today
    MobileAuto-check passed
  • Shepherd PRs

    flutter/flutter

    Automate shepherding, checking status, updating branches, and landing open PRs or approved third-party contributor PRs in the flutter/flutter repository using the gh CLI.

    179k GitHub stars~1.7k tokensUpdated today
    MobileAuto-check passed

More from divinevideo/divine-mobile

All 103 skills in this repo
  • Fix ArgoCD ExternalSecret deployment failing with "namespace X is not permitted in project Y".

    266 GitHub stars~931 tokensUpdated today
    Auto-check passed
  • Art Direct

    divinevideo/divine-mobile

    Art direction for any content — reads text, PDF, Word, HTML, PPT, then proposes 2-3 creative directions with photography style, mood, and visual language.

    266 GitHub stars~4.8k tokensUpdated today
    Auto-check passed
  • Async Await Null Race Condition

    divinevideo/divine-mobile

    Fix "Null check operator used on a null value" errors when an object is set to null during an async await.

    266 GitHub stars~881 tokensUpdated today
    Auto-check passed
  • AWS V4 Signing Custom Headers Gcs

    divinevideo/divine-mobile

    Add custom metadata headers (x-amz-meta-) to AWS v4 signed requests for GCS S3-compatible API.

    266 GitHub stars~1k tokensUpdated today
    Auto-check passed
  • Bash Herestring Newline Secrets

    divinevideo/divine-mobile

    Fix password/secret authentication failures caused by trailing newlines when creating Google Cloud secrets (or similar) with bash here-strings.

    266 GitHub stars~791 tokensUpdated today
    Auto-check passed
  • Fix silent video/media processing failures caused by URL extraction code that filters on file extensions (.mp4, .webm, .webp).

    266 GitHub stars~1.1k tokensUpdated today
    Auto-check passed

Works with

Categories

Questions about Flutter Async Test Unhandled Future Rejection

What does Flutter Async Test Unhandled Future Rejection do?

Fix flaky Flutter/Dart tests that fail in CI but pass locally due to unhandled Future rejections. Flutter Async Test Unhandled Future Rejection is an agent skill from divinevideo/divine-mobile. Fix flaky Flutter/Dart tests that fail in CI but pass locally due to unhandled Future rejections.

When should I use Flutter Async Test Unhandled Future Rejection?

Flutter Async Test Unhandled Future Rejection fits situations like: test passes locally but fails in CI with cryptic errors; test creates Futures that will throw errors (e.g; network calls to fake URLs); even with .catchError() at the end.

How do I install Flutter Async Test Unhandled Future Rejection in Claude Code?

Run `npx skills add divinevideo/divine-mobile --skill flutter-async-test-unhandled-future-rejection -a claude-code`. Or copy the skill folder (.agents/skills/flutter-async-test-unhandled-future-rejection in divinevideo/divine-mobile) into .claude/skills/flutter-async-test-unhandled-future-rejection in your project. Claude Code loads it when a task matches its description.

How do I install Flutter Async Test Unhandled Future Rejection in Codex?

Run `npx skills add divinevideo/divine-mobile --skill flutter-async-test-unhandled-future-rejection -a codex`. Or copy the skill folder (.agents/skills/flutter-async-test-unhandled-future-rejection in divinevideo/divine-mobile) into .agents/skills/flutter-async-test-unhandled-future-rejection in your project. Codex loads it when a task matches its description.

Can I use Flutter Async Test Unhandled Future Rejection in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add divinevideo/divine-mobile --skill flutter-async-test-unhandled-future-rejection -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/flutter-async-test-unhandled-future-rejection, .gemini/skills/flutter-async-test-unhandled-future-rejection, .github/skills/flutter-async-test-unhandled-future-rejection and .opencode/skills/flutter-async-test-unhandled-future-rejection in your project.

What does Flutter Async Test Unhandled Future Rejection need to run?

Going by SKILL.md and its folder, Flutter Async Test Unhandled Future Rejection needs the command-line tools its instructions call (flutter).

Does Flutter Async Test Unhandled Future Rejection access the network?

SKILL.md names 2 domains. As links in the text: dart.dev and api.flutter.dev. This is read from the text; nothing was executed.

Is Flutter Async Test Unhandled Future Rejection safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Flutter Async Test Unhandled Future Rejection use?

Flutter Async Test Unhandled Future Rejection is published under the MPL-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Flutter Async Test Unhandled Future Rejection use?

About 1.6k tokens (SKILL.md is roughly 6.4k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Flutter Async Test Unhandled Future Rejection?

Skills that share tags, products or a category with Flutter Async Test Unhandled Future Rejection: Engine Whats New (flutter/flutter, 179k stars), Flutter Cherry Pick (flutter/flutter, 179k stars), Upgrade Browser (flutter/flutter, 179k stars) and Bump Dart (flutter/flutter, 179k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Flutter Async Test Unhandled Future Rejection?

divinevideo (a GitHub organization) maintains it in divinevideo/divine-mobile, which has 266 GitHub stars. The repository holds 103 skills in this directory. The repository was last updated on October 8, 2026.

Source: divinevideo/divine-mobile on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.