Devolutions/UniGetUI · warning · InvalidOperationException

tailLines must be greater than or equal to zero.

Error message

tailLines must be greater than or equal to zero.

What it means

Thrown by GetOperationOutput when the optional tailLines parameter is provided but is negative (less than zero). tailLines controls how many trailing output lines to return; zero means return all lines (the snapshot method treats <=0 as 'all'), while negative is rejected as invalid input.

Source

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

            CanCancel = info.CanCancel,
            CanForget = info.CanForget,
            AvailableQueueActions = info.AvailableQueueActions,
            AvailableRetryModes = info.AvailableRetryModes,
            Package = info.Package,
            ManagerName = info.ManagerName,
            SourceName = info.SourceName,
            Output = tracked.GetOutputSnapshot(),
        };
    }

    public static IpcOperationOutputResult GetOperationOutput(
        string operationId,
        int? tailLines = null
    )
    {
        if (tailLines.HasValue && tailLines.Value < 0)
        {
            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."

View on GitHub (pinned to 9b1d7d0eab)

Solutions

  1. Pass null or omit tailLines entirely to get all output lines.
  2. Pass 0 or a positive integer to limit the tail.
  3. Ensure any computed tailLines value is clamped to >= 0 before calling.

Example fix

// before
var result = IpcOperationApi.GetOperationOutput(opId, tailLines: lineCount - 10);
// after — guard against negative
var tail = Math.Max(0, lineCount - 10);
var result = IpcOperationApi.GetOperationOutput(opId, tailLines: tail == 0 ? null : tail);
Defensive patterns

Strategy: validation

Validate before calling

if (tailLines.HasValue && tailLines.Value < 0)
    throw new ArgumentOutOfRangeException(nameof(tailLines), "Must be >= 0.");
// Clamp or convert to null for 'all lines'
tailLines = tailLines.HasValue && tailLines.Value <= 0 ? null : tailLines;

Prevention

When it happens

Trigger: Calling GetOperationOutput(operationId, tailLines) where tailLines is a negative integer. The parameter is nullable int, so null (omit) is valid and means 'all lines'. A value of 0 is valid and also returns all lines per the GetOutputSnapshot logic.

Common situations: IPC client passes -1 as a 'give me everything' sentinel (should use null or 0 instead). Arithmetic on line counts produces a negative value when the operation has no output yet. Misunderstanding the parameter semantics.

Related errors


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