Devolutions/UniGetUI · warning · InvalidOperationException

Only queued or running operations can be canceled.

Error message

Only queued or running operations can be canceled.

What it means

Thrown by CancelOperation when the operation's status is not InQueue or Running (i.e., IsActive returns false). Only operations that are actively queued or executing can be canceled. Completed, failed, canceled, or forgotten operations cannot be canceled again.

Source

Thrown at src/UniGetUI.Interface.IpcApi/IpcOperationApi.cs:201

        {
            throw new InvalidOperationException("tailLines must be greater than or equal to zero.");
        }

        var tracked = GetTrackedOperation(operationId);
        return new IpcOperationOutputResult
        {
            OperationId = tracked.Operation.Metadata.Identifier,
            LineCount = tracked.GetOutputCount(),
            Output = tracked.GetOutputSnapshot(tailLines),
        };
    }

    public static IpcCommandResult CancelOperation(string operationId)
    {
        var tracked = GetTrackedOperation(operationId);
        if (!IsActive(tracked.Operation.Status))
        {
            throw new InvalidOperationException(
                "Only queued or running operations can be canceled."
            );
        }

        tracked.Operation.Cancel();
        return IpcCommandResult.Success("cancel-operation");
    }

    public static IpcCommandResult RetryOperation(string operationId, string? retryMode)
    {
        var tracked = GetTrackedOperation(operationId);
        if (IsActive(tracked.Operation.Status))
        {
            throw new InvalidOperationException(
                "Running or queued operations cannot be retried."
            );
        }

View on GitHub (pinned to 9b1d7d0eab)

Solutions

  1. Check the operation status via GetOperation or ListOperations before calling CancelOperation.
  2. Use the CanCancel flag from IpcOperationInfo as the client-side gate for the cancel button.
  3. Handle this exception gracefully in the UI — the operation already completed, so refresh its state.

Example fix

// before — cancel without status check
IpcOperationApi.CancelOperation(opId);
// after
var info = IpcOperationApi.GetOperation(opId);
if (info.CanCancel)
    IpcOperationApi.CancelOperation(opId);
Defensive patterns

Strategy: validation

Validate before calling

var info = IpcOperationApi.GetOperation(operationId);
if (!info.CanCancel)
    throw new InvalidOperationException($"Operation {operationId} cannot be canceled (status: {info.Status}).");

Try / catch

try
{
    IpcOperationApi.CancelOperation(operationId);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("cannot be canceled"))
{
    // Operation already completed — refresh UI state
    logger.LogDebug("Operation {Id} already completed", operationId);
}

Prevention

When it happens

Trigger: Calling CancelOperation with an operationId for an operation whose status is not InQueue or Running. The operation must first be found by GetTrackedOperation (which throws a different error if not tracked), then its status is checked.

Common situations: Race condition: the operation finished between the client's last status check and the cancel call. Client UI shows a stale status and the cancel button is enabled for an already-completed operation. Double-clicking cancel after the first click already succeeded.

Related errors


AI-assisted analysis of Devolutions/UniGetUI@9b1d7d0eab (2026-08-13). Data as JSON: /api/errors/5bba73fd05a4fbe8. Report an issue: GitHub.