What's Actually Happening
OPA (Open Policy Agent) policy evaluation fails when evaluating policy against input data. Policy returns error instead of allow/deny decision.
The Error You'll See
```bash $ opa eval -d policy.rego -d input.json 'data.policy.allow'
Error: policy.rego:10: rego_type_error: undefined ref: data.policy.allow ```
Syntax error:
Error: policy.rego:5: rego_parse_error: unexpected tokenRuntime error:
Error: policy.rego:15: rego_runtime_error: object key conflictDivision error:
Error: arithmetic error: division by zeroWhy This Happens
- 1.Undefined reference - Referencing variable/package not defined
- 2.Syntax error - Invalid Rego syntax
- 3.Type mismatch - Wrong type in operations
- 4.Key conflict - Duplicate keys in object
- 5.Input missing - Required input data not provided
- 6.Division by zero - Arithmetic operation with zero divisor
Step 1: Check Rego Syntax
```bash # Parse policy file: opa parse policy.rego
# Check for syntax errors: opa check policy.rego
# Verbose check: opa check policy.rego --verbose
# Parse with details: opa parse policy.rego --format json
# Common syntax errors:
# Missing package declaration: # Every .rego file must start with package declaration package policy # Required
# Wrong comparison operator: # BAD: if input.user == "admin" # GOOD: input.user == "admin"
# Missing braces: # BAD: allow if input.role == "admin" # GOOD: allow if { input.role == "admin" }
# Test policy: opa test policy.rego -v
# Run all tests: opa test . -v ```
Step 2: Verify Package and Rule Names
```bash # Check package name: head -1 policy.rego # Should be: package <name>
# Evaluate with correct package path: opa eval -d policy.rego 'data.policy.allow' # Must match package name: policy
# If package is auth: package auth # Evaluate: opa eval -d policy.rego 'data.auth.allow'
# List all packages: opa eval -d policy.rego 'data'
# Check rule defined: opa eval -d policy.rego 'data.policy' # Shows all rules in package
# Common mistake: # File named auth.rego but package is policy # Then data.auth.allow is undefined
# Correct: file auth.rego with package auth ```
Step 3: Check Input Data
```bash # Verify input format: cat input.json | jq .
# Evaluate with input: opa eval -d policy.rego -d input.json 'data.policy.allow'
# Check input structure: opa eval -d policy.rego -d input.json 'input'
# Verify input matches policy expectations: # Policy expects: allow if { input.user.role == "admin" }
# Input must have: { "user": { "role": "admin" } }
# Missing input field: opa eval -d policy.rego -d input.json 'input.user' # If undefined, check input.json structure
# Set input inline: opa eval -d policy.rego 'input.user.role == "admin"' --input '{"user":{"role":"admin"}}'
# Check input type: opa eval -d policy.rego -d input.json 'json.marshal(input)' ```
Step 4: Debug Rule Evaluation
```bash # Trace evaluation: opa eval -d policy.rego -d input.json 'data.policy.allow' --explain
# Full trace: opa eval -d policy.rego -d input.json 'data.policy.allow' --explain=full
# Show all decisions: opa eval -d policy.rego -d input.json 'data.policy' --explain=full
# Debug partial evaluation: opa eval -d policy.rego -d input.json 'data.policy.allow' --explain=notes
# Profile evaluation: opa eval -d policy.rego -d input.json 'data.policy.allow' --profile
# Check intermediate values: opa eval -d policy.rego -d input.json 'data.policy.user_allowed'
# Show all defined rules: opa eval -d policy.rego 'data.policy'
# Evaluate specific rule conditions: opa eval -d policy.rego -d input.json 'input.user.role == "admin"' ```
Step 5: Fix Type Errors
```bash # Common type errors:
# String vs number comparison: # BAD: input.age == "25" # If input.age is number 25
# GOOD: input.age == 25
# Type check: opa eval -d policy.rego -d input.json 'is_string(input.user)' opa eval -d policy.rego -d input.json 'is_number(input.age)'
# Null handling: allow if { input.user != null # Check user exists input.user.role == "admin" }
# Array vs object: # BAD: input.users.role # If users is array
# GOOD: input.users[0].role # Or iterate: some user in input.users user.role == "admin"
# Type conversion: to_number(input.age) to_string(input.id) ```
Step 6: Fix Key Conflicts
```bash # Object key conflict error: # Happens when building object with duplicate keys
# BAD: roles := { "admin": true, "admin": false # Duplicate key! }
# GOOD: roles := { "admin": true, "user": false }
# Dynamic key conflicts: # BAD: allow := { input.user: true, input.user: false }
# Use comprehensions carefully: # Can create duplicates if iteration produces same keys
roles := { user.name: user.role | some user in input.users } # If two users have same name, key conflict!
# Fix by using unique identifier: roles := { user.id: user.role | some user in input.users }
# Check for conflicts in sets: # Sets allow no duplicates by definition roles := { "admin", "admin" } # Result: {"admin"} - set removes duplicate ```
Step 7: Handle Arithmetic Errors
```bash # Division by zero:
# BAD: ratio := input.total / input.count # If input.count is 0, error
# GOOD: ratio := input.total / input.count if input.count != 0 default ratio := 0
# Or use conditional: ratio := input.total / input.count if { input.count > 0 } default ratio := 0
# Modulo by zero: # Same issue with modulo operator %
# Safe arithmetic: safe_div(a, b) := a / b if b != 0 default safe_div(a, b) := 0
# Use in policy: ratio := safe_div(input.total, input.count)
# Check values before operations: allow if { input.count != 0 ratio := input.total / input.count ratio > 0.5 } ```
Step 7: Handle Arithmetic Errors
```bash # Division by zero:
# BAD: ratio := input.total / input.count # If input.count is 0, error
# GOOD: ratio := input.total / input.count if input.count != 0 default ratio := 0
# Or use conditional: ratio := input.total / input.count if { input.count > 0 } default ratio := 0
# Modulo by zero: # Same issue with modulo operator %
# Safe arithmetic: safe_div(a, b) := a / b if b != 0 default safe_div(a, b) := 0
# Use in policy: ratio := safe_div(input.total, input.count)
# Check values before operations: allow if { input.count != 0 ratio := input.total / input.count ratio > 0.5 } ```
Step 8: Fix Reference Errors
```bash # Undefined reference error:
# Check what's defined: opa eval -d policy.rego 'data.policy' # Shows all rules
# Check input structure: opa eval -d policy.rego -d input.json 'input'
# Use default for undefined: default allow := false
allow if { input.user.role == "admin" } # If input.user undefined, allow stays false
# Safe navigation with conditions: allow if { object.get(input, ["user", "role"], "") == "admin" }
# Or check existence: allow if { input.user # Checks user exists input.user.role == "admin" }
# Walk to find nested values: some path, value in object.walk(input) path == ["user", "role"] value == "admin" ```
Step 9: Test Policy Locally
```bash # Create test file: # policy_test.rego
package policy
test_allow_admin if { allow with input as {"user": {"role": "admin"}} }
test_deny_user if { not allow with input as {"user": {"role": "user"}} }
test_deny_missing_user if { not allow with input as {} }
# Run tests: opa test policy.rego policy_test.rego -v
# Run all tests: opa test . -v
# Coverage report: opa test . --coverage
# Output coverage: opa test . --coverage --format=json
# Check specific test: opa test . -v --run test_allow_admin
# Benchmarks: opa test . --bench ```
Step 10: OPA Policy Verification Script
```bash # Create verification script: cat << 'EOF' > /usr/local/bin/check-opa-policy.sh #!/bin/bash
POLICY=${1:-"policy.rego"} INPUT=${2:-"input.json"}
echo "=== Policy Syntax Check ===" opa check $POLICY 2>&1
echo "" echo "=== Parse Policy ===" opa parse $POLICY 2>&1 | head -20
echo "" echo "=== Policy Structure ===" opa eval -d $POLICY 'data' 2>&1 | head -20
echo "" echo "=== Input Data ===" if [ -f "$INPUT" ]; then cat $INPUT | jq . else echo "No input file" fi
echo "" echo "=== Evaluation Test ===" opa eval -d $POLICY -d $INPUT 'data.policy.allow' 2>&1
echo "" echo "=== Evaluation Trace ===" opa eval -d $POLICY -d $INPUT 'data.policy.allow' --explain=notes 2>&1 | head -30
echo "" echo "=== All Rules ===" opa eval -d $POLICY 'data.policy' 2>&1
echo "" echo "=== Test Results ===" opa test $POLICY -v 2>&1 | tail -20
echo "" echo "=== Recommendations ===" echo "1. Ensure package declaration at file start" echo "2. Verify rule names match evaluation path" echo "3. Check input data structure matches policy" echo "4. Add default values for undefined references" echo "5. Handle division by zero with conditions" echo "6. Avoid duplicate object keys" echo "7. Write unit tests for policy rules" EOF
chmod +x /usr/local/bin/check-opa-policy.sh
# Usage: /usr/local/bin/check-opa-policy.sh policy.rego input.json ```
OPA Policy Checklist
| Check | Expected |
|---|---|
| Syntax | No parse errors |
| Package declared | package statement present |
| Rule defined | Evaluation path exists |
| Input format | Matches policy expectations |
| Type correct | String/number/array match |
| No key conflicts | Unique object keys |
| No division by zero | Safe arithmetic |
| Tests pass | All tests succeed |
Verify the Fix
```bash # After fixing OPA policy evaluation error
# 1. Parse policy opa parse policy.rego // No errors
# 2. Check policy opa check policy.rego // No errors
# 3. Evaluate policy opa eval -d policy.rego -d input.json 'data.policy.allow' // Returns true or false (not error)
# 4. Run tests opa test policy.rego -v // All tests pass
# 5. Check coverage opa test . --coverage // Coverage report generated
# 6. Integration test # Gatekeeper or OPA integration works ```
Related Issues
- [Fix OPA Rego Syntax Error](/articles/fix-opa-rego-syntax-error)
- [Fix OPA Gatekeeper Violation False](/articles/fix-opa-gatekeeper-violation-false)
- [Fix Kyverno Policy Not Enforcing](/articles/fix-kyverno-policy-not-enforcing)