alibaba/spring-ai-alibaba · error · IllegalStateException
Schedule already started
Error message
Schedule already started
What it means
ScheduledAgentTask.start() throws IllegalStateException if the task was already started (started flag true). Each ScheduledAgentTask instance guards against double-scheduling, which would otherwise register duplicate executions with the taskScheduler.
Source
Thrown at spring-ai-alibaba-graph-core/src/main/java/com/alibaba/cloud/ai/graph/scheduling/ScheduledAgentTask.java:72
private final String taskId;
public ScheduledAgentTask(CompiledGraph graph, ScheduleConfig config) {
this.graph = graph;
this.config = config;
ScheduledAgentManager scheduledAgentManager = ScheduledAgentManagerFactory.getInstance().getManager();
this.taskScheduler = scheduledAgentManager.getTaskScheduler();
// Register with the active manager
this.taskId = scheduledAgentManager.registerTask(this);
log.debug("Created ScheduledAgentTask with ID: {}", taskId);
}
/**
* Start the scheduled execution
*/
public ScheduledAgentTask start() {
if (started) {
throw new IllegalStateException("Schedule already started");
}
switch (config.getMode()) {
case CRON:
scheduledFuture = taskScheduler.schedule(this::executeGraph,
new CronTrigger(config.getCronExpression()));
break;
case FIXED_DELAY:
scheduledFuture = taskScheduler.scheduleWithFixedDelay(this::executeGraph,
Instant.now().plusMillis(config.getInitialDelay()), Duration.ofMillis(config.getFixedDelay()));
break;
case FIXED_RATE:
scheduledFuture = taskScheduler.scheduleAtFixedRate(this::executeGraph,
Instant.now().plusMillis(config.getInitialDelay()), Duration.ofMillis(config.getFixedRate()));
break;
case ONE_TIME:
scheduledFuture = taskScheduler.schedule(this::executeGraph,
Instant.now().plusMillis(config.getInitialDelay()));View on GitHub (pinned to f82da0b50f)
Solutions
- Track started tasks and only call start() once per instance (check a started flag or idempotent wrapper)
- Create a new ScheduledAgentTask instance for restart instead of reusing the old one
- Call stop()/cancel on the existing task before starting a new instance
- Wrap start() in an idempotency guard: if (taskStartedIds.add(taskId)) task.start();
Example fix
// before
task.start();
task.start(); // throws
// after
if (!task.isStarted()) {
task.start();
} Defensive patterns
Strategy: validation
Validate before calling
if (!task.isStarted()) {
task.start();
} Type guard
boolean canStart(ScheduledAgentTask t) {
return t != null && !t.isStarted();
} Try / catch
try {
task.start();
} catch (IllegalStateException e) {
if ("Schedule already started".equals(e.getMessage())) {
// idempotent: already running, safe to ignore
} else throw e;
} Prevention
- Wrap start() calls in idempotent guards keyed by task id
- Create a new ScheduledAgentTask for every (re)schedule instead of restarting the old one
- Centralize task startup in one lifecycle method
- Track started task ids in a Set to avoid double invocation
When it happens
Trigger: Calling start() twice on the same ScheduledAgentTask instance — e.g. restart logic that re-invokes start on an existing task, or configuration code that starts tasks both in initialization and in a scheduler scan.
Common situations: Application hot-reload calling start again; idempotency wrappers missing around task bootstrap; tests running setup twice.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Default Scheduled Agent Manager is shut down
- ConfigAgentWatcher is already started
- Cannot create update map after mergeAll() has been called
- mergeAll() can only be called once
- Shell session not initialized. Call initialize() before exec
AI-assisted analysis of alibaba/spring-ai-alibaba@f82da0b50f (2026-09-09).
Data as JSON: /api/errors/c5a67e9f24ce029a.
Report an issue: GitHub.