asyncredux-user-exceptions
Handle user-facing errors with UserException. Covers throwing UserException from actions, setting up UserExceptionDialog, customizing error dialogs with `onShowUserExceptionDialog`, and using UserExceptionAction for non-interrupting error display.
Best use case
asyncredux-user-exceptions is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Handle user-facing errors with UserException. Covers throwing UserException from actions, setting up UserExceptionDialog, customizing error dialogs with `onShowUserExceptionDialog`, and using UserExceptionAction for non-interrupting error display.
Teams using asyncredux-user-exceptions should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/asyncredux-user-exceptions/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How asyncredux-user-exceptions Compares
| Feature / Agent | asyncredux-user-exceptions | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Handle user-facing errors with UserException. Covers throwing UserException from actions, setting up UserExceptionDialog, customizing error dialogs with `onShowUserExceptionDialog`, and using UserExceptionAction for non-interrupting error display.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# UserException in AsyncRedux
`UserException` is a special error type for user-facing errors that should be displayed to the user rather than logged as bugs. These represent issues the user can address or should be informed about.
## Throwing UserException from Actions
Throw `UserException` when an action encounters a user-facing error:
```dart
class TransferMoney extends AppAction {
final double amount;
TransferMoney(this.amount);
AppState? reduce() {
if (amount == 0) {
throw UserException('You cannot transfer zero money.');
}
return state.copy(cash: state.cash - amount);
}
}
```
For async actions with validation:
```dart
class SaveUser extends AppAction {
final String name;
SaveUser(this.name);
Future<AppState?> reduce() async {
if (name.length < 4)
throw UserException('Name must have at least 4 letters.');
await saveUser(name);
return null;
}
}
```
## Converting Errors to UserException
Use `addCause()` to preserve the original error while showing a user-friendly message:
```dart
class ConvertAction extends AppAction {
final String text;
ConvertAction(this.text);
Future<AppState?> reduce() async {
try {
var value = int.parse(text);
return state.copy(counter: value);
} catch (error) {
throw UserException('Please enter a valid number')
.addCause(error);
}
}
}
```
## Setting Up UserExceptionDialog
Wrap your home page with `UserExceptionDialog` below both `StoreProvider` and `MaterialApp`:
```dart
Widget build(context) {
return StoreProvider<AppState>(
store: store,
child: MaterialApp(
home: UserExceptionDialog<AppState>(
child: MyHomePage(),
),
),
);
}
```
If you omit the `onShowUserExceptionDialog` parameter, a default dialog appears with the error message and an OK button.
## Customizing Error Dialogs
Use `onShowUserExceptionDialog` to create custom error dialogs:
```dart
UserExceptionDialog<AppState>(
onShowUserExceptionDialog: (BuildContext context, UserException exception) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Error'),
content: Text(exception.message ?? 'An error occurred'),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text('OK'),
),
],
),
);
},
child: MyHomePage(),
)
```
For non-standard error presentation (like snackbars or banners), you can modify the behavior by accessing the `didUpdateWidget` method in a custom implementation.
## UserExceptionAction for Non-Interrupting Errors
Use `UserExceptionAction` to show an error dialog without throwing an exception or stopping action execution:
```dart
// Show error dialog without failing the action
dispatch(UserExceptionAction('Please enter a valid number'));
```
This is useful when you want to notify the user of an issue mid-action while continuing execution:
```dart
class ConvertAction extends AppAction {
final String text;
ConvertAction(this.text);
Future<AppState?> reduce() async {
var value = int.tryParse(text);
if (value == null) {
// Shows dialog but action continues
dispatch(UserExceptionAction('Invalid number, using default'));
value = 0;
}
return state.copy(counter: value);
}
}
```
## Reusable Error Handling with Mixins
Create mixins to standardize UserException conversion across actions:
```dart
mixin ShowUserException on AppAction {
String getErrorMessage();
Object? wrapError(Object error, StackTrace stackTrace) {
return UserException(getErrorMessage()).addCause(error);
}
}
class ConvertAction extends AppAction with ShowUserException {
final String text;
ConvertAction(this.text);
@override
String getErrorMessage() => 'Please enter a valid number.';
Future<AppState?> reduce() async {
var value = int.parse(text); // Any error becomes UserException
return state.copy(counter: value);
}
}
```
## Global Error Handling with GlobalWrapError
Handle third-party or framework errors uniformly across all actions:
```dart
var store = Store<AppState>(
initialState: AppState.initialState(),
globalWrapError: MyGlobalWrapError(),
);
class MyGlobalWrapError extends GlobalWrapError {
@override
Object? wrap(Object error, StackTrace stackTrace, ReduxAction<dynamic> action) {
if (error is PlatformException &&
error.code == 'Error performing get') {
return UserException('Check your internet connection')
.addCause(error);
}
// Return the error unchanged for other cases
return error;
}
}
```
**Processing order**: Action's `wrapError()` -> `GlobalWrapError` -> `ErrorObserver`
## Error Queue
Thrown `UserException` instances are stored in a dedicated error queue within the store. The queue is consumed by `UserExceptionDialog` to display error messages. You can configure the maximum queue capacity in the Store constructor.
## Checking Failed Actions in Widgets
Use these methods to check action failure status and display errors inline:
```dart
Widget build(BuildContext context) {
if (context.isFailed(SaveUserAction)) {
var exception = context.exceptionFor(SaveUserAction);
return Column(
children: [
Text('Failed: ${exception?.message}'),
ElevatedButton(
onPressed: () {
context.clearExceptionFor(SaveUserAction);
context.dispatch(SaveUserAction(name));
},
child: Text('Retry'),
),
],
);
}
return Text('User saved successfully');
}
```
Note: Error states automatically clear when an action is redispatched, so manual cleanup before retry is usually unnecessary.
## Testing UserExceptions
Test that actions throw `UserException` correctly:
```dart
test('should throw UserException for invalid input', () async {
var store = Store<AppState>(initialState: AppState.initialState());
var status = await store.dispatchAndWait(TransferMoney(0));
expect(status.isCompletedFailed, isTrue);
var error = status.wrappedError;
expect(error, isA<UserException>());
expect((error as UserException).message, 'You cannot transfer zero money.');
});
```
Test multiple exceptions using the error queue:
```dart
test('should collect multiple UserExceptions', () async {
var store = Store<AppState>(initialState: AppState.initialState());
await store.dispatchAndWaitAll([
InvalidAction1(),
InvalidAction2(),
InvalidAction3(),
]);
var errors = store.errors;
expect(errors.length, 3);
expect(errors[0].message, 'First error message');
});
```
## References
URLs from the documentation:
- https://asyncredux.com/sitemap.xml
- https://asyncredux.com/flutter/advanced-actions/errors-thrown-by-actions
- https://asyncredux.com/flutter/basics/failed-actions
- https://asyncredux.com/flutter/testing/testing-user-exceptions
- https://asyncredux.com/flutter/basics/wait-fail-succeed
- https://asyncredux.com/flutter/advanced-actions/wrapping-the-reducer
- https://asyncredux.com/flutter/basics/storeRelated Skills
design-and-user-experience-guidelines
Specifies design and user experience guidelines, including dark mode compatibility, responsive design, performance optimization, modern UI, and accessibility. This rule promotes a user-friendly and vi
Clarify Epic/Feature/UserStory/Task ticketing guidance in SKILL
No description provided.
asyncredux-wait-fail-succeed
Show loading states and handle action failures in widgets. Covers `isWaiting(ActionType)` for spinners, `isFailed(ActionType)` for error states, `exceptionFor(ActionType)` for error messages, and `clearExceptionFor()` to reset failure states.
asyncredux-wait-condition
Use `waitCondition()` inside actions to pause execution until state meets criteria. Covers waiting for price thresholds, coordinating between actions, and implementing conditional workflows.
asyncredux-sync-actions
Creates AsyncRedux (Flutter) synchronous actions that update state immediately by implementing reduce() to return a new state.
asyncredux-streams-timers
Manage Streams and Timers with AsyncRedux. Covers creating actions to start/stop streams, storing stream subscriptions in store props, dispatching actions from stream callbacks, and proper cleanup with disposeProps().
asyncredux-state-access
Access store state in widgets using `context.state`, `context.select()`, and `context.read()`. Covers when to use each method, setting up BuildContext extensions, and optimizing widget rebuilds with selective state access.
asyncredux-setup
Initialize, setup and configure AsyncRedux in a Flutter app. Use it whenever starting a new AsyncRedux project, or when the user requests.
asyncredux-selectors
Create and cache selectors for efficient state access. Covers writing selector functions, caching with `cache1` and `cache2`, the reselect pattern, and avoiding repeated computations in widgets.
asyncredux-persistence
Implement local state persistence using Persistor. Covers creating a custom Persistor class, implementing `readState()`, `persistDifference()`, `deleteState()`, using LocalPersist helper, throttling saves, and pausing/resuming persistence with app lifecycle.
asyncredux-optimistic-update-mixin
Add the OptimisticUpdate mixin for instant UI feedback before server confirmation. Covers immediate state changes, automatic rollback on failure, and optionally notifying users of rollback.
asyncredux-navigation
Handle navigation through actions using NavigateAction. Covers setting up the navigator key, dispatching NavigateAction for push/pop/replace, and testing navigation in isolation.