# Rego Keyword Examples: not

The `not` keyword is the primary means of expressing [negation](/docs/policy-language#negation) in Rego. Similar to other keywords in Rego, it can also make your policies more 'English-like' and thus easier to read.

```
allow if {    not input.user.external}
```

## Examples

Checking for undefined

One of the most important use cases for `not` is checking for undefined values. In this example a policy uses `not` to deny any request without an `email` set. Even if a value is not used in the policy, it might be important information for the [decision log](/docs/management-decision-logs).

Try updating the example `input.json`, changing `e_mail` to `email`. When `e_mail` is set, then `email` is undefined and `not` checks for that in the first rule.

policy.rego

```
package playdeny contains "missing email" if not input.emaildeny contains "under 18" if input.age < 18
```

Output

{
  "deny": \[
    "missing email"
  \]
}

input.json

```
{  "e_mail": "oops@example.com",  "age": 20}
```

data.json

```
{}
```

[Open in OPA Playground](https://play.openpolicyagent.org/?state=eyJpIjoie1xuICBcImVfbWFpbFwiOiBcIm9vcHNAZXhhbXBsZS5jb21cIixcbiAgXCJhZ2VcIjogMjBcbn0iLCJkIjoie30iLCJwIjoicGFja2FnZSBwbGF5XG5cbmRlbnkgY29udGFpbnMgXCJtaXNzaW5nIGVtYWlsXCIgaWYgbm90IGlucHV0LmVtYWlsXG5cbmRlbnkgY29udGFpbnMgXCJ1bmRlciAxOFwiIGlmIGlucHV0LmFnZSA8IDE4XG4ifQ==)

Negation with not

The `not` keyword is also useful for all kinds of negations. Use negations when you want to required the opposite of a statement.

policy.rego

```
package playimport future.keywords.notdeny contains "must be staff" if {	not "staff" in input.roles}deny contains "must be example.com account" if {	not endswith(input.email, "@example.com")}deny contains "cannot be accesed over VPN" if {	not input.is_vpn}
```

Output

{
  "deny": \[
    "cannot be accesed over VPN",
    "must be example.com account",
    "must be staff"
  \]
}

input.json

```
{  "roles": [    "intern"  ],  "email": "alice@foo.example.com",  "via_vpn": true}
```

data.json

```
{}
```

[Open in OPA Playground](https://play.openpolicyagent.org/?state=eyJpIjoie1xuICBcInJvbGVzXCI6IFtcbiAgICBcImludGVyblwiXG4gIF0sXG4gIFwiZW1haWxcIjogXCJhbGljZUBmb28uZXhhbXBsZS5jb21cIixcbiAgXCJ2aWFfdnBuXCI6IHRydWVcbn0iLCJkIjoie30iLCJwIjoicGFja2FnZSBwbGF5XG5cbmltcG9ydCBmdXR1cmUua2V5d29yZHMubm90XG5cbmRlbnkgY29udGFpbnMgXCJtdXN0IGJlIHN0YWZmXCIgaWYge1xuXHRub3QgXCJzdGFmZlwiIGluIGlucHV0LnJvbGVzXG59XG5cbmRlbnkgY29udGFpbnMgXCJtdXN0IGJlIGV4YW1wbGUuY29tIGFjY291bnRcIiBpZiB7XG5cdG5vdCBlbmRzd2l0aChpbnB1dC5lbWFpbCwgXCJAZXhhbXBsZS5jb21cIilcbn1cblxuZGVueSBjb250YWlucyBcImNhbm5vdCBiZSBhY2Nlc2VkIG92ZXIgVlBOXCIgaWYge1xuXHRub3QgaW5wdXQuaXNfdnBuXG59XG4ifQ==)

## Improved Negation Semantics

The `future.keywords.not` import fixes a long-standing semantic issue with negation in Rego.

### The problem with legacy negation

Without the import, the compiler expands a negated composite expression like `not f(g(input.x))` into a series of sub-expressions evaluated _before_ the `not`:

```
__local0__ = input.xg(__local0__, __local1__)not f(__local1__)
```

If any sub-expression fails — for example, `input.x` is undefined or `g` produces an undefined result — the entire rule fails rather than the `not` succeeding. This is unintuitive: the user's intent is "the condition does not hold," but an undefined intermediate value causes a silent failure instead of the expected `not` result.

### Implicit body wrapping

With `import future.keywords.not`, composite-expression negation wraps the full compiler expansion in an implicit body:

```
not { __local0__ = input.x; g(__local0__, __local1__); f(__local1__) }
```

Now, if _any_ sub-expression is undefined or fails, the body is unsatisfiable and the `not` expression succeeds; matching the intuition that "the condition does not hold."

```
{  "user": "cesar"}
```

```
package negationimport future.keywords.not# Succeeds when input.role is undefined OR when lookup/admin failrestricted if {    not admin(lookup(input.user))}groups := {    "admin": ["alice"],    "user": ["bob"]}lookup(user) := group if {    some group, members in groups    user in members}admin(group) if group in ["admin", "sudo"]
```

important

Notice how if we remove the `future.keywords.not` import in the above policy, the `restricted` rule starts failing. This is a consequence of the `lookup()` function failing with an `undefined` value.

### Explicit negation bodies

The import also enables a `not` expression to take a curly-brace-enclosed body instead of a single expression:

```
{  "servers": [    {      "name": "web1",      "listener": {        "port": 80,        "protocol": "tcp"      }    },    {      "name": "web2",      "listener": {        "port": 443,        "protocol": "tcp"      }    },    {      "name": "web3",      "listener": {        "port": 443,        "protocol": "udp"      }    }  ]}
```

```
package negationimport future.keywords.not# Deny any server that doesn't listen on TCP on port 443deny contains $"server {server.name} is misconfigured" if {    some server in input.servers    not {        # If any of the following expressions fail, the 'not' succeeds        listener := server.listener        listener.port == 443        listener.protocol == "tcp"    }}
```

The `not` succeeds when the body is **unsatisfiable**; no combination of variable bindings makes every expression in the body true.

Variables declared inside the body (`listener` above) are scoped locally and are not visible outside the `not` block.