{"record":{"id":"595f7a8201655d5d","repo":"influxdata/influxdb","slug":"running-a-cron-trigger-that-is-finished","errorCode":null,"errorMessage":"running a cron trigger that is finished.","messagePattern":"running a cron trigger that is finished\\.","errorType":"exception","errorClass":"PluginError","httpStatus":null,"severity":"error","filePath":"influxdb3_processing_engine/src/plugins.rs","lineNumber":135,"sourceCode":"        }\n    });\n\n    Ok(())\n}\n\nasync fn run_schedule_event_source_loop(\n    mut runner: ScheduleTriggerRunner,\n    time_provider: Arc<dyn TimeProvider>,\n    scheduler: Scheduler,\n    key: TriggerKey,\n    cancel: CancellationToken,\n) -> Result<(), PluginError> {\n    while let Some(next_run_instant) = runner.next_run_time() {\n        tokio::select! {\n            _ = cancel.cancelled() => break,\n            _ = time_provider.sleep_until(next_run_instant) => {\n                let Some(scheduled_at) = runner.next_trigger_time else {\n                    return Err(anyhow!(\"running a cron trigger that is finished.\").into());\n                };\n                runner.advance_time();\n                if scheduler\n                    .enqueue(TriggerInvocation::new(\n                        key,\n                        TriggerPayload::Schedule { scheduled_at },\n                    ))\n                    .await\n                    .is_err()\n                {\n                    break;\n                }\n            }\n        }\n    }\n    Ok(())\n}\n","sourceCodeStart":117,"sourceCodeEnd":153,"githubUrl":"https://github.com/influxdata/influxdb/blob/06200ef96ba82c5f6727e5038a83af8e722c6875/influxdb3_processing_engine/src/plugins.rs#L117-L153","documentation":"Raised by run_schedule_event_source_loop when the cron runner's schedule is exhausted (next_run_time yields an instant, but next_trigger_time is None), i.e. the loop woke for a scheduled instant but the schedule is finished and cannot provide a scheduled_at. Treated as an internal invariant violation via anyhow!, surfaced as PluginError.","triggerScenarios":"A cron schedule with a finite end date whose final run has been consumed; clock/time-provider anomalies where next_run_time and next_trigger_time disagree.","commonSituations":"Using a bounded cron schedule (cron crate supports 'TO' ranges/limited schedules) for a long-lived trigger; system clock changes.","solutions":["Use an unbounded recurring cron expression (e.g. '0 * * * *') instead of a finite/one-shot schedule.","Recreate the trigger with a fresh future schedule if a one-shot run was intended.","If seen with a normal recurring schedule, check NTP clock sync and report upstream."],"exampleFix":"// before\nSCHEDULE '0 0 1 1 * 2026'  // bounded, finishes after last run\n// after\nSCHEDULE '0 0 1 1 *'       // recurring every Jan 1","handlingStrategy":"try-catch","validationCode":"use cron::Schedule;\nfn is_unbounded(expr: &str) -> bool {\n    expr.parse::<Schedule>().map(|s| s.upcoming(chrono::Utc).take(5).count() == 5).unwrap_or(false)\n}","typeGuard":null,"tryCatchPattern":"if let Err(e) = run_schedule_event_source(...).await {\n    if e.to_string().contains(\"finished\") { recreate_trigger_with_recurring_schedule()?; }\n}","preventionTips":["Use recurring, unbounded cron expressions for long-lived triggers.","Recreate triggers whose finite schedule windows have expired.","Keep clocks NTP-synchronized."],"tags":["cron","scheduling","async"],"backgroundTag":"internal-invariant-violation","analyzedSha":"06200ef96ba82c5f6727e5038a83af8e722c6875","analyzedAt":"2026-09-19T12:55:30.003Z","contentChangedAt":"2026-09-19T12:55:30.003Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}