{"record":{"id":"3182c9e752bee4aa","repo":"kestra-io/kestra","slug":"only-queued-execution-can-be-unqueued","errorCode":null,"errorMessage":"Only QUEUED execution can be unqueued","messagePattern":"Only QUEUED execution can be unqueued","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/io/kestra/core/services/ConcurrencyLimitService.java","lineNumber":28,"sourceCode":"import jakarta.inject.Inject;\nimport jakarta.inject.Singleton;\n\n@Singleton\npublic class ConcurrencyLimitService {\n\n    private static final Set<State.Type> VALID_TARGET_STATES = EnumSet.of(State.Type.RUNNING, State.Type.CANCELLED, State.Type.FAILED);\n\n    @Inject\n    private ExecutionQueuedStateStore executionQueuedStateStore;\n\n    /**\n     * Unqueue a queued execution.\n     *\n     * @throws IllegalArgumentException in case the execution is not queued or is transitioned to an unsupported state.\n     */\n    public Execution unqueue(Execution execution, State.Type state) {\n        if (execution.getState().getCurrent() != State.Type.QUEUED) {\n            throw new IllegalArgumentException(\"Only QUEUED execution can be unqueued\");\n        }\n\n        state = (state == null) ? State.Type.RUNNING : state;\n\n        // Validate the target state, throwing an exception if the state is invalid\n        if (!VALID_TARGET_STATES.contains(state)) {\n            throw new IllegalArgumentException(\"Invalid target state: \" + state + \". Valid states are: \" + VALID_TARGET_STATES);\n        }\n\n        executionQueuedStateStore.remove(execution);\n\n        return execution.withState(state);\n    }\n}\n","sourceCodeStart":10,"sourceCodeEnd":43,"githubUrl":"https://github.com/kestra-io/kestra/blob/823fada9274c4f9c251ea0a516460a4f7d958032/core/src/main/java/io/kestra/core/services/ConcurrencyLimitService.java#L10-L43","documentation":"The `ConcurrencyLimitService.unqueue()` method transitions a queued execution to a target state. The execution must currently be in the `QUEUED` state — if it is in any other state (RUNNING, SUCCESS, FAILED, etc.), an `IllegalArgumentException` is thrown. Only QUEUED executions are tracked in the `ExecutionQueuedStateStore` and can be removed/unqueued.","triggerScenarios":"Calling `unqueue()` on an execution that has already been unqueued (moved to RUNNING). Calling it on an execution that was never queued (e.g., it went straight to RUNNING because no concurrency limit applied). A race condition where the execution was unqueued by another thread before this call.","commonSituations":"A concurrency limit was reached, an execution was queued, then manually or automatically unqueued; a second unqueue attempt fails. The execution state changed between a status check and the unqueue call.","solutions":["Check `execution.getState().getCurrent() == State.Type.QUEUED` before calling `unqueue()`.","Handle the case gracefully — if the execution is already past QUEUED, it may have been processed by another caller.","Use optimistic locking or idempotency checks to handle race conditions."],"exampleFix":"// before\nexecutionService.unqueue(execution, State.Type.RUNNING);\n// after\nif (execution.getState().getCurrent() == State.Type.QUEUED) {\n    execution = executionService.unqueue(execution, State.Type.RUNNING);\n}","handlingStrategy":"type-guard","validationCode":"// Check execution state before unqueueing\nimport io.kestra.core.models.flows.State;\n\npublic static boolean isUnqueueable(Execution execution) {\n    return execution.getState().getCurrent() == State.Type.QUEUED;\n}\n\nif (isUnqueueable(execution)) {\n    execution = concurrencyLimitService.unqueue(execution, targetState);\n}","typeGuard":"import { State } from './types';\n\nfunction isQueued(state: State.Type): boolean {\n    return state === 'QUEUED';\n}","tryCatchPattern":"try {\n    execution = concurrencyLimitService.unqueue(execution, state);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().equals(\"Only QUEUED execution can be unqueued\")) {\n        // already unqueued by another caller, or state changed\n        log.info(\"Execution {} is no longer queued, skipping unqueue\", execution.getId());\n    } else {\n        throw e;\n    }\n}","preventionTips":["Always check `execution.getState().getCurrent() == State.Type.QUEUED` before unqueueing.","Handle the case where the execution was already unqueued by another caller (race condition).","Log the current state when the guard fails to aid debugging."],"tags":["concurrency","execution","state-machine","validation"],"backgroundTag":null,"analyzedSha":"823fada9274c4f9c251ea0a516460a4f7d958032","analyzedAt":"2026-08-14T06:15:17.947Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}