Skip to content

Errors

import 'package:gisila_queue/gisila_queue.dart';
Exception Raised by You should
TaskFailureException AsyncResult.get, GroupResult.get Catch where you wait for results.
ResultTimeoutException AsyncResult.get Catch; the task may still finish.
UnencodableArgumentsException delay, applyAsync, send Fix the arguments (programming error).
RetryTask ctx.retry() inside a handler Throw (via ctx.retry); don’t catch.
RejectTask your handler Throw to drop or requeue.
TaskTimeLimitExceededException worker, on timeLimit Nothing; it becomes the failure reason.
UnknownTaskException exported; not thrown by the worker
StateError AsyncResult members, send(dedupeKey:) or chord().apply() without a result backend; GisilaRose.start() refusing an unauthenticated public bind Fix configuration.
ArgumentError GisilaQueue/Beat when component prefixes differ; GisilaQueue.redis with an empty namespace; task() with a duplicate name Fix configuration.
abstract class GisilaQueueException implements Exception {
final String message;
}

Base of every exception below except UnencodableArgumentsException. toString() is '<Type>: <message>'.

class TaskFailureException extends GisilaQueueException {
const TaskFailureException(String taskId, String error, {Object? cause});
final String taskId;
final Object? cause;
}

Thrown by AsyncResult.get when the task ended in failure (message is the stored error) or revoked (message task was revoked). The worker stores errors as strings, so cause is null here: inspect message.

try {
await r.get();
} on TaskFailureException catch (e) {
log.warning('task ${e.taskId}: ${e.message}');
}
class ResultTimeoutException extends GisilaQueueException {
const ResultTimeoutException(String taskId, Duration timeout);
final String taskId;
final Duration timeout;
}

get waited timeout without a terminal state. The task is not cancelled.

class UnencodableArgumentsException implements Exception {
const UnencodableArgumentsException(String taskName, Object cause);
final String taskName;
final Object cause;
}

Arguments contain a value JSON can’t encode (DateTime, a custom class, a cycle). Raised before anything is enqueued.

class RetryTask extends GisilaQueueException {
RetryTask({Duration? countdown, Object? cause});
final Duration? countdown;
final Object? cause;
}

Thrown by ctx.retry(). The worker retries while retries < maxRetries, otherwise records retries exhausted (n): cause. Throwing it directly is equivalent. Don’t catch it in your handler’s own try/catch (e) around ctx.retry() by accident; rethrow if you catch broadly.

class RejectTask extends GisilaQueueException {
const RejectTask({bool requeue = false, String? reason});
final bool requeue;
}

When to use: RejectTask() to drop a message you can tell is pointless (stored as revoked with reason, not dead-lettered). RejectTask(requeue: true) to put it back right away without counting a retry, for example when a resource is temporarily held; after maxRequeues it is failed and dead-lettered.

app.task<void>('orders.ship', (ctx) async {
final order = await orders.find(ctx.arg(0) as int);
if (order == null) throw const RejectTask(reason: 'order deleted');
if (order.locked) throw const RejectTask(requeue: true);
await ship(order);
});
class TaskTimeLimitExceededException extends GisilaQueueException {
const TaskTimeLimitExceededException(String taskId, Duration limit);
final String taskId;
final Duration limit;
}

Used by the worker when a handler passes Task.timeLimit. It is handled like any thrown error (retried with autoRetry, else failed). The handler is not stopped.

class UnknownTaskException extends GisilaQueueException {
const UnknownTaskException(String taskName);
final String taskName;
}

Exported for custom code. The worker handles unknown task names itself (requeue with backoff, then dead-letter) and does not throw this.

See also: Retries and acks.