---
title: "Forbid unexpected keys"
description: "Seal a map with `close` so a typo'd or invented key is refused instead of absorbed."
source: "https://aontu.dev/how-to/forbid-unexpected-keys/"
---

# Forbid unexpected keys

Seal a map with \`close\` so a typo'd or invented key is refused instead of absorbed.

Rendered from [`docs/how-to/forbid-unexpected-keys.md`](https://github.com/aontu-lang/aontu/blob/main/docs/how-to/forbid-unexpected-keys.md) in the engine repository, where a correction belongs, and where the test suite executes every example on this page.

Maps are open by default: a key the schema never mentioned unifies in without comment. That openness is what makes composition work, and it is exactly wrong for config. Seal the map with `close`:

```aontu
config: close({ host:string port:integer })
config: { host:h port:1 }
```

```json
{
  "config": {
    "host": "h",
    "port": 1
  }
}
```

Declared keys compose as before. Now add a key the schema does not declare. Write this as `config.aon`:

```aontu
config: close({ host:string port:integer })
config: { host:h port:1 debug:true }
```

```sh
$ aontu config.aon
[aontu/closed]: Cannot resolve value at path $.config.debug
...
$ echo $?
1
```

The error names the key, which is the point: an absorbed `debug` would have run in production doing nothing.

`open(x)` lifts a seal again, so a schema you import closed can be extended deliberately:

```aontu
a: open(close({ x:1 })) & { y:2 }
```

```json
{
  "a": {
    "x": 1,
    "y": 2
  }
}
```

What to watch for: `close` seals exactly the node it wraps, and only that node. Children generated inside it stay open unless you seal them too: around a `pack` generator that difference decides whether a misspelled override is refused or absorbed, and [seal generated children deeply](https://aontu.dev/how-to/seal-generated-children) walks through it. A list tail is also not closed by the enclosing map’s seal; the element template is: see [constrain every element of a list](https://aontu.dev/how-to/constrain-list-elements).

The semantics are specified in [Closed values: `close` / `open`](https://aontu.dev/docs/reference-language#closed-values-close--open). Closedness is also the one thing aontu and JSON Schema say identically (`additionalProperties: false`): see [export JSON Schema](https://aontu.dev/how-to/export-json-schema).
