floci-io/floci · warning · AwsException

PipelineExecutionNotStoppableException

PipelineExecutionNotStoppableException

Error message

Pipeline execution is already in a terminal state

What it means

Thrown by stopPipelineExecution (CodePipelineService.java:293) when StopPipelineExecution targets an execution whose status is already terminal (isTerminal check — Succeeded/Failed/Cancelled and similar). Once terminal, an execution can no longer transition to Stopping, matching AWS PipelineExecutionNotStoppableException.

Source

Thrown at src/main/java/io/github/hectorvent/floci/services/codepipeline/CodePipelineService.java:293

        execution.setVariables(variableList(request.path("variables")));
        Map<String, String> trigger = new LinkedHashMap<>();
        trigger.put("triggerType", "StartPipelineExecution");
        trigger.put("triggerDetail", "manual");
        if (clientToken != null) {
            trigger.put("clientRequestToken", clientToken);
        }
        execution.setTrigger(trigger);
        putExecution(execution);
        applyExecutionMode(execution);
        executor.submit(() -> runExecution(pipeline, execution));
        return mapper.createObjectNode().put("pipelineExecutionId", execution.getPipelineExecutionId());
    }

    private ObjectNode stopPipelineExecution(JsonNode request, String region, String account) {
        CodePipelineExecution execution = requireExecution(
                account, region, text(request, "pipelineName"), text(request, "pipelineExecutionId"));
        if (isTerminal(execution.getStatus())) {
            throw new AwsException("PipelineExecutionNotStoppableException",
                    "Pipeline execution is already in a terminal state", 400);
        }
        execution.setStopRequested(true);
        execution.setAbandon(request.path("abandon").asBoolean(false));
        execution.setStatus("Stopping");
        execution.setStatusSummary(request.path("reason").asText("Stop requested."));
        execution.setLastUpdateTime(now());
        if (execution.isAbandon()) {
            execution.getActionExecutions().stream()
                    .filter(a -> "InProgress".equals(a.getStatus()))
                    .forEach(a -> {
                        a.setStatus("Abandoned");
                        a.setLastUpdateTime(now());
                    });
        }
        putExecution(execution);
        return mapper.createObjectNode().put("pipelineExecutionId", execution.getPipelineExecutionId());
    }

View on GitHub (pinned to 62ff490619)

Solutions

  1. Re-fetch the execution status (ListPipelineExecutions/GetPipelineExecution) immediately before stopping and skip if terminal.
  2. Treat this error as benign in retry logic — catch by code PipelineExecutionNotStoppableException and exit cleanly.
  3. Reduce the check-to-stop window by issuing the stop from the same process that observes InProgress.

Example fix

// before
client.stop_pipeline_execution(pipelineName='p', pipelineExecutionId=exec_id)  # may already be terminal

// after
status = client.get_pipeline_execution(pipelineName='p', pipelineExecutionId=exec_id) \
    ['pipelineExecution']['status']
if status in ('InProgress', 'Stopping'):
    client.stop_pipeline_execution(pipelineName='p', pipelineExecutionId=exec_id)
Defensive patterns

Strategy: try-catch

Validate before calling

status = client.get_pipeline_execution(
    pipelineName='p', pipelineExecutionId=exec_id)['pipelineExecution']['status']
if status in ('InProgress', 'Stopping'):
    client.stop_pipeline_execution(pipelineName='p', pipelineExecutionId=exec_id)

Try / catch

try:
    client.stop_pipeline_execution(pipelineName='p', pipelineExecutionId=exec_id)
except ClientError as e:
    if e.response['Error']['Code'] == 'PipelineExecutionNotStoppableException':
        logger.info('execution already finished; nothing to stop')
    else:
        raise

Prevention

When it happens

Trigger: StopPipelineExecution(pipelineName, pipelineExecutionId) called after the execution finished; dashboards/monitors racing the pipeline's natural completion; retrying a stop that succeeded earlier.

Common situations: An operator UI polling executions where the run completes between refresh and click; automation retry loops that re-send stop on failure of a prior (already-resolved) request; double-invocation of a deploy-cancel webhook.

Related errors


AI-assisted analysis of floci-io/floci@62ff490619 (2026-08-14). Data as JSON: /api/errors/dca848393ffbc742. Report an issue: GitHub.