Policy Validation
Type mismatches, contradictions, and redundancies.
Callcium validates policies at build time through semantic analysis. When you call PolicyBuilder.build(), the PolicyValidator inspects every constraint for type errors, logical contradictions, redundancies, and vacuous conditions — then reverts if any issue is found.
Using validation
PolicyBuilder.build() validates by default and reverts on any issue:
// Reverts with ValidationError if issues are found.
bytes memory policy = PolicyBuilder
.create("approve(address,uint256)")
.add(arg(0).isIn(trustedSpenders))
.build();To inspect issues without reverting, use PolicyBuilder.validate():
Issue[] memory issues = PolicyBuilder
.create("approve(address,uint256)")
.add(arg(0).isIn(trustedSpenders))
.validate();
for (uint256 i = 0; i < issues.length; i++) {
// Handle each issue: severity, category, code, message.
}PolicyBuilder.buildUnsafe() skips validation entirely. The resulting policy may be invalid.
Issue structure
Each issue contains:
| Field | Type | Description |
|---|---|---|
severity | IssueSeverity | Error, Warning, or Info. |
category | IssueCategory | TypeMismatch, Contradiction, Redundancy, or Vacuity. |
groupIndex | uint32 | Constraint group where the issue was found. |
constraintIndex | uint32 | Constraint index within the group. |
code | bytes32 | Machine-readable identifier. |
value1 | bytes32 | First value involved in the issue. |
value2 | bytes32 | Second value involved in the issue. |
message | string | Human-readable description. |
Severity levels
PolicyBuilder.build() reverts on any issue regardless of severity. Use PolicyBuilder.validate() to inspect issues without reverting.
| Severity | Meaning |
|---|---|
| Error | The policy is logically invalid (contradictions, type mismatches). |
| Warning | The policy is valid but contains redundant or suspicious constraints. |
| Info | The constraint is technically correct but vacuous (always true). |
Issue catalog
Type compatibility
Operators, operands, and paths that do not fit the descriptor's declared types. All are Error severity.
| Code | Description |
|---|---|
VALUE_OP_ON_DYNAMIC | Value operator used on a dynamic type. |
VALUE_OP_ON_COMPOSITE | Value operator used on a composite type. |
NUMERIC_OP_ON_NON_NUMERIC | Comparison operator used on a non-numeric type. |
BITMASK_ON_INVALID | Bitmask operator used on an incompatible type. |
IN_ON_BOOL | isIn/notIn used on a bool. |
LENGTH_ON_STATIC | Length operator used on a non-dynamic type. |
CONTEXT_TYPE_MISMATCH | Context operand paired with a target type its property cannot match. |
UNKNOWN_OPERATOR | Unrecognized operator code. |
NON_CANONICAL_OPERAND | Operand word is not the canonical encoding for the declared type. |
UNNAVIGABLE_PATH | Path does not navigate the descriptor. |
NESTED_QUANTIFIER | Path applies more than one quantifier. |
HINT_MISMATCH | Stored hint differs from the path compiled against the descriptor. |
Contradictions
Make the constraint set unsatisfiable. All are Error severity.
Bound contradictions
These apply to both numeric values and lengths. Length variants have the LENGTH_ prefix.
| Code | Description |
|---|---|
EQ_NEQ_CONTRADICTION | eq(v) and neq(v) on same path. |
LENGTH_EQ_NEQ_CONTRADICTION | lengthEq(v) and lengthNeq(v) on same path. |
CONFLICTING_EQUALITY | Multiple eq() with different values. |
CONFLICTING_LENGTH | Multiple lengthEq() with different values. |
OUT_OF_PHYSICAL_BOUNDS | Value outside the type's physical range. |
OUT_OF_PHYSICAL_LENGTH_BOUNDS | Length value outside physical range. |
IMPOSSIBLE_GT | gt() on type maximum. |
IMPOSSIBLE_LT | lt() on type minimum. |
IMPOSSIBLE_LENGTH_GT | lengthGt() on maximum length. |
IMPOSSIBLE_LENGTH_LT | lengthLt(0) on minimum length. |
BOUNDS_EXCLUDE_EQUALITY | eq() value excluded by a bound. |
BOUNDS_EXCLUDE_LENGTH | lengthEq() value excluded by a bound. |
IMPOSSIBLE_RANGE | Lower bound exceeds upper bound. |
IMPOSSIBLE_LENGTH_RANGE | Lower length bound exceeds upper. |
Set contradictions
| Code | Description |
|---|---|
SET_EXCLUDES_EQUALITY | Set operation excludes existing eq() value. |
EMPTY_SET_INTERSECTION | Multiple isIn() sets have no intersection. |
SET_FULLY_EXCLUDED | All isIn() values excluded by neq/notIn. |
Bitmask contradictions
| Code | Description |
|---|---|
BITMASK_CONTRADICTION | Conflicting bitmaskAll/bitmaskNone bits. |
BITMASK_ANY_IMPOSSIBLE | bitmaskAny bits all forbidden by bitmaskNone. |
Structural errors
| Code | Description |
|---|---|
UNSORTED_IN_SET | isIn/notIn set is not strictly sorted and deduplicated. |
EMPTY_GROUP | Group contains zero constraints. |
Redundancies
Constraints with no effect because they are subsumed. All are Warning severity.
Bound redundancies
| Code | Description |
|---|---|
DOMINATED_BOUND | Numeric bound dominated by a stricter bound. |
DOMINATED_LENGTH_BOUND | Length bound dominated by a stricter bound. |
REDUNDANT_BOUND | Numeric bound redundant because eq() is set. |
REDUNDANT_LENGTH_BOUND | Length bound redundant because lengthEq() is set. |
FUSIBLE_RANGE | gte() and lte() fuse into a single between(). |
FUSIBLE_LENGTH_RANGE | lengthGte() and lengthLte() fuse into a single lengthBetween(). |
Set redundancies
| Code | Description |
|---|---|
SET_REDUCTION | notIn() value was present in isIn() set. |
SET_REDUNDANCY | isIn() sets partially overlap. |
SET_PARTIALLY_EXCLUDED | Some isIn() values excluded by neq/notIn. |
Bitmask redundancies
| Code | Description |
|---|---|
REDUNDANT_BITMASK | Bitmask operation subsumed by existing mask. |
Duplicates
| Code | Description |
|---|---|
DUPLICATE_CONSTRAINT | Identical operator appears twice. |
Vacuity
Constraints that are always true. All are Info severity.
| Code | Description |
|---|---|
VACUOUS_GTE | gte() on type minimum (always true). |
VACUOUS_LTE | lte() on type maximum (always true). |
VACUOUS_LENGTH_GTE | lengthGte(0) (always true). |
VACUOUS_LENGTH_LTE | lengthLte() on maximum (always true). |
VACUOUS_NEGATED_RANGE | Negated between() with low > high excludes nothing. |
VACUOUS_NEGATED_LENGTH_RANGE | Negated lengthBetween() with low > high excludes nothing. |
Compatibility
Constructs that cross a normative limit or that behave differently than most authors intend. QUANTIFIER_OVER_STATIC_LIMIT is Error severity; the rest are Warning. Normative limits are fixed for this format version and every conformant enforcer applies the same values, so buildUnsafe() is the only way to emit a policy that crosses one.
| Code | Description |
|---|---|
PATH_DEPTH_EXCEEDED | Path deeper than MAX_PATH_DEPTH; a policy declaring it is not well-formed (PWF-17). |
QUANTIFIER_OVER_STATIC_LIMIT | Quantifier over a static array longer than MAX_QUANTIFIED_ARRAY_LENGTH (PV-7). |
UNKNOWN_CONTEXT_PROPERTY | Context property ID outside the assigned set (PWF-25). |
NEGATION_UNDER_ANY | Negated operator under any(); a single decoy element satisfies the rule. |