Skip to content

Global Configuration

Static properties that configure behavior for all commands in your app. Set these once, typically in your app's main() function before calling runApp().

Overview

PropertyTypeDefaultPurpose
globalExceptionHandlerFunction?nullGlobal error handler for all commands
globalErrorsStreamN/AObservable stream of all globally-routed errors
errorFilterDefaultErrorFilterGlobalIfNoLocalErrorFilter()Default error filter
assertionsAlwaysThrowbooltrueAssertionErrors bypass filters
reportAllExceptionsboolfalseOverride filters, report all errors
detailedStackTracesbooltrueEnhanced stack traces
loggingHandlerFunction?nullHandler for all command executions
reportErrorHandlerExceptionsToGlobalHandlerbooltrueReport error handler exceptions
useChainCaptureboolfalseExperimental detailed traces

Complete Setup Example

Here's a typical setup configuring multiple global properties:

dart
void main() {
  // Configure global command_it settings before runApp

  // 1. Global exception handler - called based on ErrorFilter configuration
  Command.globalExceptionHandler = (error, stackTrace) {
    debugPrint('Command error: ${error.error}');
    debugPrint('Command: ${error.command}');
    debugPrint('Parameter: ${error.paramData}');

    // In production, send to Sentry
    // Sentry.captureException(error.error, stackTrace: stackTrace);
  };

  // 2. Default error filter - determines error handling strategy
  Command.errorFilterDefault = const GlobalIfNoLocalErrorFilter();

  // 3. Logging handler - log all command executions
  Command.loggingHandler = (commandName, result) {
    if (kDebugMode) {
      debugPrint(
          'Command executed: ${result.isRunning ? "started" : "completed"}');
      if (result.hasError) {
        debugPrint('  Error: ${result.error}');
      }
    }
  };

  // 4. Detailed stack traces - strip framework noise (default: true)
  Command.detailedStackTraces = true;

  // 5. Assertions always throw - bypass error filters for assertions (default: true)
  Command.assertionsAlwaysThrow = true;

  // 6. Report all exceptions - ensure all errors reach global handler (for debugging)
  Command.reportAllExceptions = kDebugMode;

  // 7. Report error handler exceptions - if local error handler throws
  Command.reportErrorHandlerExceptionsToGlobalHandler = true;

  runApp(MyApp());
}

globalExceptionHandler

Global error handler called for all command errors:

dart
static void Function(CommandError<dynamic> error, StackTrace stackTrace)?
  globalExceptionHandler;

Set this once in your main() function to handle errors globally:

dart
void main() {
  Command.globalExceptionHandler = (error, stackTrace) {
    loggingService.logError(error.error, stackTrace);
    Sentry.captureException(error.error, stackTrace: stackTrace);
  };

  runApp(MyApp());
}

When it's called:

  • Depends on ErrorFilter configuration (default: when no local listeners exist)
  • Always called when reportAllExceptions: true

See: Error Handling - Global Error Handler for complete documentation including usage examples, error context details, and patterns.

globalErrors

Observable stream of all command errors routed to the global handler:

dart
static Stream<CommandError<dynamic>> get globalErrors

Perfect for reactive error monitoring, analytics, crash reporting, and global UI notifications:

dart
// Example: Global error toast in root widget
class MyApp extends WatchingWidget {
  @override
  Widget build(BuildContext context) {
    registerStreamHandler<Stream<CommandError>, CommandError>(
      target: Command.globalErrors,
      handler: (context, snapshot, cancel) {
        if (snapshot.hasData) {
          ScaffoldMessenger.of(context).showSnackBar(
            SnackBar(content: Text('Error: ${snapshot.data!.error}')),
          );
        }
      },
    );
    return MaterialApp(home: HomePage());
  }
}

Key points:

  • Broadcast stream (multiple listeners supported)
  • Emits when ErrorFilter routes errors to global handler
  • Does NOT emit for debug-only reportAllExceptions
  • Use with globalExceptionHandler for comprehensive error handling

See: Error Handling - Global Errors Stream for complete documentation including use cases, stream behavior, and integration patterns.

errorFilterDefault

Default ErrorFilter used when no individual filter is specified per command:

dart
static ErrorFilter errorFilterDefault = const GlobalIfNoLocalErrorFilter();

Built-in Error Filters

  • GlobalIfNoLocalErrorFilter() (default) - Try local handlers first, fallback to global
  • LocalErrorFilter() - Only call local handlers (.errors or .results listeners)
  • GlobalErrorFilter() - Only call global exception handler
  • LocalAndGlobalErrorFilter() - Call both local and global handlers

Example

dart
// Change default behavior for all commands
Command.errorFilterDefault = const LocalErrorFilter();

See: Error Handling for complete ErrorFilter documentation and custom filters.

assertionsAlwaysThrow

AssertionErrors bypass ErrorFilters and are always rethrown:

dart
static bool assertionsAlwaysThrow = true;

Default: true (recommended)

Why This Exists

AssertionErrors indicate programming mistakes (like assert(condition) failures). They should be caught immediately during development, not silently swallowed by error filters.

Example

dart
// Treat assertions like any other error (not recommended)
Command.assertionsAlwaysThrow = false;

Recommendation: Keep this true to catch bugs early in development.

reportAllExceptions

Ensure every error calls globalExceptionHandler for debugging:

dart
static bool reportAllExceptions = false;

Default: false

How It Works

When true, every error calls globalExceptionHandler immediately, in addition to normal ErrorFilter processing.

Execution flow:

  1. Error occurs in command
  2. globalExceptionHandler is called (if reportAllExceptions: true)
  3. ErrorFilter runs normally (determines local/global handling)
  4. globalExceptionHandler may be called again (if ErrorFilter says to)

Result: ErrorFilters are NOT bypassed - they still control local handlers and can trigger a second global handler call.

Common Pattern: Debug vs Production

dart
// In main.dart
Command.reportAllExceptions = kDebugMode;

What this does:

  • Development (kDebugMode = true): ALL errors reach global handler for visibility
  • Production (kDebugMode = false): Only errors routed by ErrorFilter reach global handler

When to Use

  • Debugging error handling - Ensure no errors are silently swallowed
  • Development mode - See all errors regardless of ErrorFilter configuration
  • Verifying crash reporting - Confirm all errors reach your analytics/crash reporting

Important: Potential Duplicate Calls

dart
Command.reportAllExceptions = true;
Command.errorFilterDefault = const ErrorHandlerGlobal(); // Also calls global handler

// Result: globalExceptionHandler called TWICE for each error!
// 1. From reportAllExceptions
// 2. From ErrorFilter

Solution: In production, use either reportAllExceptions OR ErrorFilters that call global, not both

detailedStackTraces

Clean up stack traces by filtering out framework noise:

dart
static bool detailedStackTraces = true;

Default: true (recommended)

What It Does

Uses the stack_trace package to filter and simplify stack traces.

Without detailedStackTraces (false) - raw stack trace:

#0      Object._throw (dart:core-patch/object_patch.dart:51)
#1      _Future._propagateToListeners (dart:async/future_impl.dart)
#2      _Future._completeError (dart:async/future_impl.dart)
#3      Zone.run (dart:async/zone.dart:1518)
#4      _rootRun (dart:async/zone.dart:1426)
... [50+ lines of framework internals]
#42     my_file.dart:42  ← Your actual code buried deep

With detailedStackTraces (true) - filtered and simplified:

#0      my_file.dart:42
#1      manager.dart:156
#2      widget.dart:89
... [Only relevant frames, framework noise removed]

What gets filtered:

  • stack_trace package internal frames
  • Zone-related frames (async framework)
  • _rootRun and similar async framework calls
  • command_it internal _run method frames

The result is also "tersed" - duplicate/redundant frames are removed.

Performance

Stack trace processing has minimal overhead. Only disable if profiling shows it's a bottleneck (rare).

loggingHandler

Handler called for every command execution (running, success, error):

dart
static void Function(CommandResult<dynamic, dynamic> result)? loggingHandler;

Default: null (no logging)

Analytics Integration Example

dart
void setupLoggingHandler() {
  Command.loggingHandler = (commandName, result) {
    // Log all command executions to analytics

    // Track command started
    if (result.isRunning) {
      analytics.logEvent('command_started', parameters: {
        'command': commandName,
        'has_parameter': result.paramData != null,
      });
      return;
    }

    // Track command completed
    if (result.hasData) {
      analytics.logEvent('command_success', parameters: {
        'command': commandName,
        'has_data': result.data != null,
        'parameter': result.paramData?.toString(),
      });
      return;
    }

    // Track command error
    if (result.hasError) {
      analytics.logEvent('command_error', parameters: {
        'command': commandName,
        'error_type': result.error.runtimeType.toString(),
        'parameter': result.paramData?.toString(),
        'had_previous_data': result.data != null,
      });
    }
  };
}

What Data Is Available

CommandResult<TParam, TResult> provides:

  • .isRunning - Whether command is currently executing
  • .hasData - Whether command has result data
  • .hasError - Whether command failed
  • .error - The error object (if any)
  • .data - The result data (if any)
  • .paramData - Parameter passed to command

Use Cases

  • Analytics - Track command execution metrics
  • Performance monitoring - Measure command execution time
  • Debugging - Log all command activity
  • Audit trails - Record user actions

reportErrorHandlerExceptionsToGlobalHandler

If a local error handler throws an exception, report it to globalExceptionHandler:

dart
static bool reportErrorHandlerExceptionsToGlobalHandler = true;

Default: true (recommended)

What This Catches

dart
command.errors.listen((error, _) {
  // Local error handler
  throw Exception('Oops, error handler has a bug!'); // ← This gets caught
});

With reportErrorHandlerExceptionsToGlobalHandler: true:

  • Exception thrown in error handler is caught
  • Sent to globalExceptionHandler
  • Original error is stored in CommandError.originalError

Why This Matters

Error handlers can have bugs too. This prevents error handling code from crashing your app.

useChainCapture

Experimental: Preserve stack traces across async boundaries to show where commands were called:

dart
static bool useChainCapture = false;

Default: false

What it does:

When enabled, preserves the call stack from where the command was invoked, even when the exception happens inside an async function. Without this, you often get an "async gap" - losing the stack trace context showing which code called the command.

Uses Dart's Chain.capture() mechanism to maintain the full stack trace across async boundaries.

Example without useChainCapture:

#0  ApiClient.fetch (api_client.dart:42)
#1  <async gap>

Example with useChainCapture:

#0  ApiClient.fetch (api_client.dart:42)
#1  _fetchDataCommand.run (data_manager.dart:156)
#2  DataScreen.build.<anonymous> (data_screen.dart:89)
#3  ... (full call chain preserved)

Status: Experimental feature that may change or be removed in future versions.

Not recommended for production use - may have performance implications.

Common Configuration Patterns

Development Mode

For maximum visibility during development:

dart
void configureDevelopmentMode() {
  // Development: verbose logging and error reporting

  // Report ALL exceptions - even if error filters would swallow them
  Command.reportAllExceptions = true;

  // Detailed stack traces - strip framework noise for easier debugging
  Command.detailedStackTraces = true;

  // Assertions always throw - catch programming errors immediately
  Command.assertionsAlwaysThrow = true;

  // Log every command execution for debugging
  Command.loggingHandler = (commandName, result) {
    final timestamp = DateTime.now().toIso8601String();
    debugPrint('[$timestamp] Command execution:');
    debugPrint('  isRunning: ${result.isRunning}');
    debugPrint('  hasData: ${result.hasData}');
    debugPrint('  hasError: ${result.hasError}');
    if (result.paramData != null) {
      debugPrint('  param: ${result.paramData}');
    }
    if (result.hasError) {
      debugPrint('  error: ${result.error}');
    }
  };

  // Global error handler - print detailed error information
  Command.globalExceptionHandler = (error, stackTrace) {
    debugPrint('═══ COMMAND ERROR ═══');
    debugPrint('Command: ${error.command}');
    debugPrint('Error: ${error.error}');
    debugPrint('Parameter: ${error.paramData}');
    debugPrint('Stack trace:');
    debugPrint(stackTrace.toString());
    debugPrint('═══════════════════');
  };

  // Default filter: try local, fallback to global
  Command.errorFilterDefault = const GlobalIfNoLocalErrorFilter();
}

Characteristics:

  • Report ALL errors (bypass filters)
  • Verbose logging for every command
  • Detailed stack traces
  • Comprehensive error context

Production Mode

For production with crash reporting integration:

dart
void configureProductionMode() {
  // Production: crash reporting only, respect error filters

  // Don't report all exceptions - respect error filters
  Command.reportAllExceptions = false;

  // Keep detailed stack traces for crash reports
  Command.detailedStackTraces = true;

  // Assertions should throw - catch critical bugs
  Command.assertionsAlwaysThrow = true;

  // Global error handler - send to Sentry
  Command.globalExceptionHandler = (error, stackTrace) {
    Sentry.captureException(
      error.error,
      stackTrace: stackTrace,
      withScope: (scope) {
        scope.setTag('command', error.command ?? 'unknown');
        scope.setContexts('command_context', {
          'parameter': error.paramData?.toString(),
          'error_reaction': error.errorReaction.toString(),
        });
      },
    );
  };

  // Default filter: local errors stay local, unhandled go global
  Command.errorFilterDefault = const GlobalIfNoLocalErrorFilter();

  // If error handler itself throws, report to global handler
  Command.reportErrorHandlerExceptionsToGlobalHandler = true;
}

Characteristics:

  • Respect error filters (don't report everything)
  • Send errors to crash reporting service
  • Detailed stack traces for debugging production issues
  • No verbose logging (keep production lean)

Testing Mode

For unit/integration tests:

dart
void setupTestMode() {
  // Disable all handlers to avoid side effects in tests
  Command.globalExceptionHandler = null;
  Command.loggingHandler = null;

  // Let errors throw naturally for test assertions
  Command.reportAllExceptions = false;
  Command.errorFilterDefault = const ErrorHandlerNone();
}

Property Interactions

reportAllExceptions Overrides Error Filters

When reportAllExceptions: true:

dart
Command.reportAllExceptions = true;
Command.errorFilterDefault = const LocalErrorFilter(); // ← Ignored!

Every error still goes to globalExceptionHandler, regardless of filter configuration.

assertionsAlwaysThrow Bypasses Everything

dart
Command.assertionsAlwaysThrow = true; // Default
Command.errorFilterDefault = const ErrorHandlerNone(); // ← Ignored for assertions!

AssertionErrors are always rethrown, even if filters would swallow them.

Error Handler Exception Reporting

dart
Command.reportErrorHandlerExceptionsToGlobalHandler = true;
Command.globalExceptionHandler = (error, stackTrace) {
  // Receives both:
  // 1. Normal command errors
  // 2. Exceptions thrown by local error handlers

  if (error.originalError != null) {
    // This error came from a buggy error handler
    print('Error handler threw: ${error.error}');
    print('Original error was: ${error.originalError}');
  }
};

Common Mistakes

❌️ Forgetting kDebugMode for reportAllExceptions

dart
// WRONG: Always report all exceptions, even in production
Command.reportAllExceptions = true;

Problem: Production app sends every error to crash reporting, creating noise.

Solution:

dart
// ✅ Only in debug mode
Command.reportAllExceptions = kDebugMode;

❌️ Not Accessing CommandError Properties

dart
// WRONG: Only using the error object
Command.globalExceptionHandler = (commandError, stackTrace) {
  Sentry.captureException(commandError.error, stackTrace: stackTrace);
  // Missing valuable context!
};

Solution:

dart
// ✅ Use full CommandError context with Sentry
Command.globalExceptionHandler = (commandError, stackTrace) {
  Sentry.captureException(
    commandError.error,
    stackTrace: stackTrace,
    withScope: (scope) {
      scope.setTag('command', commandError.command ?? 'unknown');
      scope.setContexts('command_context', {
        'parameter': commandError.paramData?.toString(),
        'error_reaction': commandError.errorReaction.toString(),
      });
    },
  );
};

❌️ Using loggingHandler for Error Handling

dart
// WRONG: Trying to handle errors in logging handler
Command.loggingHandler = (result) {
  if (result.hasError) {
    showErrorDialog(result.error); // Don't do this!
  }
};

Problem: loggingHandler is for observability, not error handling.

Solution:

dart
// ✅ Use globalExceptionHandler for error handling
Command.globalExceptionHandler = (error, stackTrace) {
  // Handle errors here
};

// ✅ Use loggingHandler only for metrics/analytics
Command.loggingHandler = (result) {
  analytics.logEvent('command_executed', parameters: {
    'has_error': result.hasError,
  });
};

❌️ Disabling detailedStackTraces Prematurely

dart
// WRONG: Disabling without measuring
Command.detailedStackTraces = false; // "For performance"

Problem: Stack trace processing has negligible overhead. Disabling makes debugging harder.

Solution:

dart
// ✅ Only disable if profiling shows it's a bottleneck
Command.detailedStackTraces = true; // Keep the default

See Also

  • Error Handling — Learn how errorFilterDefault and globalExceptionHandler work with error filters, including custom filter creation
  • Command Properties — Instance-level properties that can override global defaults (like per-command error filters)
  • Command Basics — Start here if you're new to command_it - learn how to create and run commands before configuring globals
  • Troubleshooting — Common issues and solutions, including configuration problems

Released under the MIT License.