microsoft/semantic-kernel · error · KernelException

Only one `Always` handler is allowed in a group of event han

Error message

Only one `Always` handler is allowed in a group of event handlers.

What it means

Thrown by DeclarativeEventHandlerGroupBuilder constructor when processing a condition of type Always. NOTE: The source code checks 'if (this.DefaultHandler is not null)' but throws the 'Always' message — this is likely a bug; the guard should check AlwaysHandler. In practice, the error fires when a Default handler was already set and an Always condition is subsequently encountered, or (if the bug is fixed) when two Always conditions exist.

Source

Thrown at dotnet/src/Experimental/Process.Core/ProcessAgentBuilder.cs:302

                    }

                    if (!string.IsNullOrWhiteSpace(condition.Expression))
                    {
                        throw new KernelException("`Default` handlers must not have an eval expression.");
                    }

                    this.DefaultHandler = new DeclarativeEventHandlerBuilder(condition);
                }
                else if (condition.Type == DeclarativeProcessConditionType.Eval)
                {
                    this.EvalHandlers ??= [];
                    this.EvalHandlers.Add(new DeclarativeEventHandlerBuilder(condition));
                }
                else if (condition.Type == DeclarativeProcessConditionType.Always)
                {
                    if (this.DefaultHandler is not null)
                    {
                        throw new KernelException("Only one `Always` handler is allowed in a group of event handlers.");
                    }

                    if (!string.IsNullOrWhiteSpace(condition.Expression))
                    {
                        throw new KernelException("`Always` handlers must not have an eval expression.");
                    }

                    this.AlwaysHandler = new DeclarativeEventHandlerBuilder(condition);
                }
                else
                {
                    throw new KernelException($"Unknown condition type: {condition.Type}");
                }
            }
        }
    }

    /// <summary>

View on GitHub (pinned to c028a0c7dc)

Solutions

  1. Ensure at most one Always condition exists in the handler group and no conflicting Default handler has been registered before it.
  2. Reorder conditions so that Always comes before Default (workaround for the current DefaultHandler-guard behavior), or avoid mixing Default and Always in the same group until the guard bug is fixed.
  3. Report the guard bug upstream: the Always branch should check AlwaysHandler, not DefaultHandler.

Example fix

// before — Default registered first, then Always triggers the guard
var conditions = new List<DeclarativeProcessCondition>
{
    new() { Type = DeclarativeProcessConditionType.Default },
    new() { Type = DeclarativeProcessConditionType.Always } // throws due to guard checking DefaultHandler
};

// after — only one of Default or Always per group, or reorder so Always is first
var conditions = new List<DeclarativeProcessCondition>
{
    new() { Type = DeclarativeProcessConditionType.Always }
};
Defensive patterns

Strategy: validation

Validate before calling

public static void ValidateAlwaysCondition(List<DeclarativeProcessCondition> conditions)
{
    var alwaysCount = conditions.Count(c => c?.Type == DeclarativeProcessConditionType.Always);
    if (alwaysCount > 1)
    {
        throw new KernelException("Only one Always handler is allowed per group.");
    }
    // Workaround for the current guard bug: avoid mixing Default and Always
    var hasDefault = conditions.Any(c => c?.Type == DeclarativeProcessConditionType.Default);
    var hasAlways = conditions.Any(c => c?.Type == DeclarativeProcessConditionType.Always);
    if (hasDefault && hasAlways)
    {
        // Current implementation throws due to DefaultHandler check in Always branch.
        throw new KernelException("Mixing Default and Always in one group triggers a known guard bug.");
    }
}

Prevention

When it happens

Trigger: Passing a conditions list that includes at least one Always condition while a Default handler is already registered (due to the current guard checking DefaultHandler). If the guard is corrected to check AlwaysHandler, this would fire on duplicate Always conditions instead.

Common situations: Defining handler conditions in YAML or code that include both a Default and an Always condition in the same group. Including two Always conditions. The misleading guard makes this error appear in surprising situations where the user intended one Default + one Always, which should be valid.

Related errors


AI-assisted analysis of microsoft/semantic-kernel@c028a0c7dc (2026-08-13). Data as JSON: /api/errors/7c9d0267b9dd3428. Report an issue: GitHub.