Skip to main content

rego_recursion_error: rule {name} is recursive

An important property of Rego compared to general purpose programming languages is that policy evaluation should be known to terminate. Some common programming constructs, like while loops, or recursive references, can't reasonably provide such guarantees, and are therefore not allowed in Rego. Recursion errors in Rego happens when a rule (or function, which is a special type of rule) makes a reference to either itself, or another rule that references the rule that made the original reference.

StageCategoryMessage
compilationrego_recursion_errorrule {name} is recursive: ref -> ref [-> ...]

Examples

Recursive reference to the same rule:

package policy

rule_a := rule_a
1 error occurred: policy.rego:3: rego_recursion_error: rule data.policy.rule_a is recursive: data.policy.rule_a -> data.policy.rule_a

Recursive references between two rules:

package policy

rule_a := rule_b

rule_b := rule_a
2 errors occurred:
policy.rego:5: rego_recursion_error: rule data.policy.rule_b is recursive: data.policy.rule_b -> data.policy.rule_a -> data.policy.rule_b
policy.rego:3: rego_recursion_error: rule data.policy.rule_a is recursive: data.policy.rule_a -> data.policy.rule_b -> data.policy.rule_a

While the examples above demonstrate the principle of recursive references, the error is rarely this obvious. The most common occurrence of recursion in real-world policy happens when the global data document is referenced without a specific path, or when parts of the path are dynamic, and potentially recursive.

package policy

# recursive since `data` includes `data.policy` which is the current package
rule := data
package policy

# the compiler can't know what `input.path` might provide, so this is _potentially_
# recursive (as `input.path` could be "policy") and as such flagged by the compiler
rule := data[input.path]

How To Fix It

Ensure that dynamic references to data aren't potentially recursive by using a leading static path that doesn't match the current package name.

package policy

import future.keywords.contains
import future.keywords.if
import future.keywords.in

deny contains message if {
# `data.rules` can't be recursive since the first path component ("rules") is static
# and doesn't match the name of the current package ("policy")
some message in data.rules[_].deny
}

# not recursive regardless of `input` as "children" never references "policy"
rule := data.children[input.path]

Community

For questions, discussions and announcements related to OPA, or Styra products, services and open source projects, please join the Styra Community on Slack.