abpframework/abp · error · AbpException
Both 'Period' and 'CronExpression' are not set for {worker.G
Error message
Both 'Period' and 'CronExpression' are not set for {worker.GetType().FullName}. You must set at least one of them. What it means
Thrown by AbpTickerQBackgroundWorkerManager.AddAsync when registering an AsyncPeriodicBackgroundWorkerBase or PeriodicBackgroundWorkerBase that has neither a Period nor a CronExpression set. TickerQ needs at least one to build the scheduling function; without either, it cannot determine when to invoke the worker.
Source
Thrown at framework/src/Volo.Abp.BackgroundWorkers.TickerQ/Volo/Abp/BackgroundWorkers/TickerQ/AbpTickerQBackgroundWorkerManager.cs:49
if (worker is AsyncPeriodicBackgroundWorkerBase or PeriodicBackgroundWorkerBase)
{
int? period = null;
string? cronExpression = null;
if (worker is AsyncPeriodicBackgroundWorkerBase asyncPeriodicBackgroundWorkerBase)
{
period = asyncPeriodicBackgroundWorkerBase.Period;
cronExpression = asyncPeriodicBackgroundWorkerBase.CronExpression;
}
else if (worker is PeriodicBackgroundWorkerBase periodicBackgroundWorkerBase)
{
period = periodicBackgroundWorkerBase.Period;
cronExpression = periodicBackgroundWorkerBase.CronExpression;
}
if (period == null && cronExpression.IsNullOrWhiteSpace())
{
throw new AbpException($"Both 'Period' and 'CronExpression' are not set for {worker.GetType().FullName}. You must set at least one of them.");
}
cronExpression = cronExpression ?? GetCron(period!.Value);
var name = BackgroundWorkerNameAttribute.GetNameOrNull(worker.GetType()) ?? worker.GetType().FullName;
var config = Options.GetConfigurationOrNull(ProxyHelper.GetUnProxiedType(worker));
AbpTickerQFunctionProvider.AddFunction(name!, async (tickerQCancellationToken, serviceProvider, tickerFunctionContext) =>
{
var workerInvoker = new AbpTickerQPeriodicBackgroundWorkerInvoker(worker, serviceProvider);
await workerInvoker.DoWorkAsync(tickerFunctionContext, tickerQCancellationToken);
}, config?.Priority ?? TickerTaskPriority.LongRunning, config?.MaxConcurrency ?? 0);
AbpTickerQBackgroundWorkersProvider.BackgroundWorkers.Add(name!, new AbpTickerQCronBackgroundWorker
{
Function = name!,
CronExpression = cronExpression,
WorkerType = ProxyHelper.GetUnProxiedType(worker)
});View on GitHub (pinned to 7ed43b1931)
Solutions
- Set Period in the worker constructor: Period = 60000; (or load it from IOptions/ISettingProvider).
- Set CronExpression on the worker if you want cron-based scheduling instead of a fixed period.
- If the value should come from configuration, validate that the config key exists and is non-null before assigning.
Example fix
// before — Period never set
public class MyCleanupWorker : AsyncPeriodicBackgroundWorkerBase
{
public MyCleanupWorker()
{
// Period and CronExpression both unset
}
protected override Task DoWorkAsync(PeriodicBackgroundWorkerContext workerContext)
{
return Task.CompletedTask;
}
}
// after
public class MyCleanupWorker : AsyncPeriodicBackgroundWorkerBase
{
public MyCleanupWorker()
{
Period = 3600000; // 1 hour
}
protected override Task DoWorkAsync(PeriodicBackgroundWorkerContext workerContext)
{
return Task.CompletedTask;
}
} Defensive patterns
Strategy: validation
Validate before calling
if (worker is AsyncPeriodicBackgroundWorkerBase a && a.Period == null && a.CronExpression.IsNullOrWhiteSpace())
{
throw new InvalidOperationException($"Worker {worker.GetType().FullName} has no Period and no CronExpression. Set one before registration.");
}
await manager.AddAsync(worker, ct); Type guard
static bool IsScheduled(AsyncPeriodicBackgroundWorkerBase w) => w.Period != null || !w.CronExpression.IsNullOrWhiteSpace();
Prevention
- Always set Period or CronExpression in the worker's constructor.
- If the period comes from configuration, assert it is non-null/non-zero before assigning.
- Add a startup validation pass over all registered periodic workers to confirm scheduling is set.
When it happens
Trigger: Registering a periodic worker with the TickerQ provider where the worker's Period property was never assigned and no CronExpression was set in the constructor. This can also happen if Period is explicitly set to null or left at its default (null) in a custom worker subclass.
Common situations: Subclassing AsyncPeriodicBackgroundWorkerBase and forgetting to set Period in the constructor. Reading the period from configuration that returned null (e.g. appsettings missing the key). Using a base worker type that was intended for a different provider that does not require Period.
Related errors
- Cannot convert period: {period} to cron expression.
- Period must be greater than 0 when provided. Given value: {P
- Background job execution is disabled. This method should not
- Cannot convert period: {period} to cron expression, use Hang
- Cannot convert period: {period} to cron expression.
AI-assisted analysis of abpframework/abp@7ed43b1931 (2026-08-13).
Data as JSON: /api/errors/c2ff2f4cc805789b.
Report an issue: GitHub.