floci-io/floci · error · AwsException
InstanceRefreshInProgress
InstanceRefreshInProgress
Error message
An active instance refresh already exists for Auto Scaling group '{asgName}'. What it means
Thrown by startInstanceRefresh when an instance refresh in an active status (pending/in-progress/cancelling) already exists for the group. AWS allows only one active refresh per Auto Scaling group, so StartInstanceRefresh conflicts until the previous refresh completes, fails, or is cancelled. Floci returns InstanceRefreshInProgress (HTTP 400), matching the AWS error code.
Source
Thrown at src/main/java/io/github/hectorvent/floci/services/autoscaling/AutoScalingService.java:434
.findFirst()
.ifPresent(i -> i.setLifecycleState("Terminating"));
if (decrementDesiredCapacity) {
int newDesired = Math.max(asg.getMinSize(), asg.getDesiredCapacity() - 1);
asg.setDesiredCapacity(newDesired);
}
groups.put(asgKey(region, asg.getAutoScalingGroupName()), asg);
}
// ── Instance refreshes ────────────────────────────────────────────────────
public InstanceRefresh startInstanceRefresh(String region, String asgName, InstanceRefresh requestedRefresh) {
AutoScalingGroup asg = requireGroup(region, asgName);
boolean activeRefresh = instanceRefreshes.values().stream()
.filter(r -> region.equals(r.getRegion()))
.filter(r -> asgName.equals(r.getAutoScalingGroupName()))
.anyMatch(r -> isActiveRefreshStatus(r.getStatus()));
if (activeRefresh) {
throw new AwsException("InstanceRefreshInProgress",
"An active instance refresh already exists for Auto Scaling group '" + asgName + "'.", 400);
}
Instant now = Instant.now();
InstanceRefresh refresh = new InstanceRefresh();
refresh.setInstanceRefreshId(UUID.randomUUID().toString());
refresh.setAutoScalingGroupName(asgName);
refresh.setStrategy(normalizeRefreshStrategy(requestedRefresh.getStrategy()));
refresh.setStartTime(now);
refresh.setRegion(region);
copyDesiredConfiguration(requestedRefresh, refresh);
copyPreferences(requestedRefresh, refresh);
applyDesiredConfiguration(asg, refresh);
List<String> instanceIds = markInstancesForRefresh(asg, refresh);
if (instanceIds.isEmpty()) {
refresh.setStatus("Successful");
refresh.setStatusReason("Instance refresh completed.");View on GitHub (pinned to 62ff490619)
Solutions
- Call DescribeInstanceRefreshes and wait for the active refresh to reach a terminal status before starting a new one
- If the stale refresh should be abandoned, call CancelInstanceRefresh, wait for it to show Cancelled, then start again
- Make the start operation idempotent in your automation by checking for an active refresh first
Example fix
// before
autoscaling.startInstanceRefresh(StartInstanceRefreshRequest.builder()
.autoScalingGroupName(asgName).build());
// after
boolean active = autoscaling.describeInstanceRefreshes(DescribeInstanceRefreshesRequest.builder()
.autoScalingGroupName(asgName).build())
.instanceRefreshes().stream()
.anyMatch(r -> r.status() == Status.pending || r.status() == Status.inProgress);
if (!active) {
autoscaling.startInstanceRefresh(StartInstanceRefreshRequest.builder()
.autoScalingGroupName(asgName).build());
} Defensive patterns
Strategy: try-catch
Validate before calling
boolean active = autoscaling.describeInstanceRefreshes(DescribeInstanceRefreshesRequest.builder()
.autoScalingGroupName(asgName).build())
.instanceRefreshes().stream()
.anyMatch(r -> r.status().equals("Pending") || r.status().equals("InProgress") || r.status().equals("Cancelling"));
if (active) return existingRefreshId; Try / catch
catch (InstanceRefreshInProgressException e) { poll DescribeInstanceRefreshes until terminal, then either reuse the finished result or start a new refresh once; } Prevention
- Gate refresh-triggering pipelines on 'no active refresh' checks
- Treat StartInstanceRefresh as non-idempotent and never blind-retry it
When it happens
Trigger: Calling StartInstanceRefresh twice without the first refresh reaching a terminal status (Successful/Failed/Cancelled); rapid re-invocations from a retrying script or deployment pipeline before the rolling refresh finishes.
Common situations: CI/CD pipelines that trigger refreshes on every commit without gating on refresh status; retry logic that re-sends after a timeout even though the first call succeeded; automation that assumes refreshes are instant in the emulator.
Related errors
AI-assisted analysis of floci-io/floci@62ff490619 (2026-08-14).
Data as JSON: /api/errors/df7f4827ec2b02aa.
Report an issue: GitHub.