timeout<T> method

Future<T> timeout<T>(
  1. Future<T> operation(), {
  2. required Duration limit,
  3. required String name,
})

Runs operation, failing with AgenticTimeoutException after limit.

Implemented against Clock.delay rather than Future.timeout so that a fake clock can drive it. name appears in the error and should identify the operation, for example openai.chat.completions.

The underlying future is not cancelled — Dart futures cannot be — so operations that hold resources should also observe a CancellationToken. What this guarantees is that the caller stops waiting, not that the work stops running.

Implementation

Future<T> timeout<T>(
  Future<T> Function() operation, {
  required Duration limit,
  required String name,
}) {
  final completer = Completer<T>();
  // Both completion paths below are wired into `completer`, so the future
  // is deliberately not awaited here.
  // ignore: discarded_futures
  final work = operation();
  unawaited(
    work.then<void>(
      (value) {
        if (!completer.isCompleted) completer.complete(value);
      },
      onError: (Object error, StackTrace stackTrace) {
        if (!completer.isCompleted) {
          completer.completeError(error, stackTrace);
        }
      },
    ),
  );
  unawaited(
    delay(limit).then((_) {
      if (completer.isCompleted) return;
      completer.completeError(
        AgenticTimeoutException(
          '`$name` did not complete within '
          '${limit.inMilliseconds}ms.',
          operation: name,
          timeout: limit,
        ),
        StackTrace.current,
      );
    }),
  );
  return completer.future;
}