Condition Validation
A Condition is validated before its predicate is generated. Any broken rule throws a LogicException with the listed error code.
Rules
| Rule | Error Code |
|---|---|
Field must be non-empty and exist on T | InvalidField |
Field's first segment must not be a name the expression parser keeps for itself — new, iif, np, isnull, is, as, cast, true, false, null, in any letter case | FieldPath[{path}]StartsWithReservedName |
Between / NotBetween require exactly 2 values | RequiredTwoValue |
In / IIn / NotIn / INotIn require 1+ values | RequiredValues |
IsNull / IsNotNull require 0 values | NotRequiredValues |
| All other operators require exactly 1 value | RequiredOneValue({Operator}) |
A null value normalizes to an empty string — accepted by Text and Enum, rejected by every other DataType | InvalidFormat |
Guid values must parse as Guid | InvalidFormat |
Number values must parse as a numeric type | InvalidFormat |
Boolean values must parse as bool | InvalidFormat |
Date / DateTime values must be ISO 8601, year-first, or a format declared with DwDates.Configure | AmbiguousDateFormat for a day/month-first date, otherwise InvalidFormat |
A field the parser would read as its own is refused before the lookup
The expression parser reads its own functions and literals before it looks for a member, so a path whose first segment is one of them never reaches the member. New in 3.1.0: the library refuses it by name, with the first segment — trimmed — on
LogicException.Subject. The check sits where any path is validated, so Orders, Selects, GroupBy.Fields, AggregateBy.Field and a [DwEntity(DefaultOrder)] entry answer the same way, guarded or not. Only the first segment counts — Owner.New names the member — and it, root, parent and every predefined type name such as String or Guid are ordinary members. Rename the CLR property and map the column with [Column]. See breaking changes.A date value is read the way the predicate will read it
Validation reads a date value exactly as the predicate builder does, with the member's own
DateTime, DateTimeOffset or DateOnly type. The server's culture plays no part, so a value is accepted or refused the same way on every host, and a value that passes validation is one the builder can use. A deployment that declares a local format is no different: a format whose own text the built-in readers also read is refused when it is configured, so no declaration can make a value depend on the host's time zone. ISO 8601 ("2026-09-15", "2026-09-15T12:00:00Z") and year-first dates are accepted everywhere; "01/09/2026" is refused with AmbiguousDateFormat unless the deployment declares its order. See DataType and breaking changes.Under a strict policy, an unknown field is a policy refusal
On a query guarded by
ApplyPolicy under the Strict tier, outside a dry run, a Field that names nothing on T does not raise InvalidField. It is refused like a field denied for every feature — a PolicyException with FieldDeniedForWhere, or FieldDeniedForSegment inside a segment, and FieldPath "*" — so the answer does not say whether the field exists. Unguarded queries, the convenience tier and a dry run raise InvalidField as before. See what a strict refusal says.Related
Note
Operator/value arity is enforced before any value is parsed. A missing value for
Between raises RequiredTwoValue regardless of whether the (missing) value would have parsed. There is no separate "value is null or whitespace" failure: InvalidValue (ConditionValuesAreNullOrWhiteSpace) is defined but never thrown.