# Limits, errors and refusals

From A Little Lisp for a Small World, the Plazmi Lisp book (https://plazmi.gg/book). Checked against Plazmi Lisp version 2, 12 November 2026. The whole book as one file: https://plazmi.gg/book.md

Every run has limits, and every limit has a message. This chapter goes through them, so that when a plan or a toy stops, you know what the message means and what to change. The figures are in the limits appendix, made from the code that enforces them; this chapter shows each one at work.

## Three things every refusal says

- **What stopped it**, in a line: a word, a step, a limit.
- **Where**, when there's a where: "Step 2's pitch …", "The bell's script stopped: …".
- **"Nothing was changed."** at the end, for a plan. A plan runs all or nothing, so a refusal anywhere means nothing of the plan was kept. You can fix it and send it again without undoing anything.

## Before it runs

A plan is read and checked whole before any of it runs. These are refused at that point.

A word that isn't in the table, with the nearest words that are (chapter 1). A form the language doesn't read, even with words it has:

```clojure
(if (> (coins) 10) (chimney))
```

```text
That plan writes if in a form Plazmi Lisp doesn't take, so nothing in it ran. Nothing was changed.
```

At the top of a plan, an `if` that does something needs both branches; `(when (> (coins) 10) (chimney))` says this one.

A value the language doesn't read:

```clojure
1/2
```

```text
That plan has a value Plazmi Lisp doesn't take, like a character (\a) or a fraction written out (1/2), so nothing in it ran. Nothing was changed.
```

A step's value outside what it takes. The reply names the step by its place in the plan, and says what it takes:

```clojure
(roof {:pitch 80})
```

```text
…pitch…5 to 70…Nothing was changed.
```

```clojure
(dotimes [i 101] (inc i))
```

```text
Step 1's times is a whole number from 0 to 100. Nothing was changed.
```

```clojure
(defn seven [a b c d e f g] a)
```

```text
Step 1's params is a list of up to 6 different names. Nothing was changed.
```

Text written in a plan is any text up to 400 characters; a key or a link meant for a person isn't taken, because it never goes into the world.

## While it runs

These stop a plan partway, and nothing it did is kept.

**Steps.** A run has 100,000 steps of fuel: each call, each turn of a loop, each item made, costs one, and working through text costs one for every 100 characters. A loop with no way out runs out:

```clojure
(loop [i 0] (recur (inc i)))
```

```text
Out of fuel: the script stopped at 100000 steps. Nothing was changed.
```

Almost anything that runs away runs out of fuel first, a loop or a list that's far too long alike. Behind the fuel are two more limits, of time (3 seconds) and memory, which almost nothing reaches.

**Calls.** A function that calls itself too many times without returning stops before its fuel does (chapter 6): "That recursed too deeply."

**Values.** Some things can't be worked out:

```clojure
(/ 1 0)
```

```text
Divide by zero. Nothing was changed.
```

```clojure
(nth ["rose" "sage"] 5)
```

```text
That index is past the end of the list. Nothing was changed.
```

```clojure
(+ 1 (get {:rings 4} :chimes))
```

```text
Something there was nil (nothing) where a value was needed. Nothing was changed.
```

The last is the most common, and the plainest to prevent: `(get m :k 0)` or `(or (get m :k) 0)` gives a value when the key is missing.

**A case with no match.** A `case` with no last value, given a value none of its constants match:

```clojure
(case :snow :sunny "Bright." :rain "Wet.")
```

```text
None of that case's values matched, and it has no last value for when none does. Nothing was changed.
```

## In a toy

A toy's run has the same fuel, and the same stops. When a verb stops, the player is told, and nothing the verb did is kept:

```clojure
(defobject abacus
  {:name "Abacus"
   :state {:counts []}
   :verbs {:count (fn [self who]
                    (say self (str "The third count was " (nth (:counts self) 2) ".")))}})
```

```text
The abacus is ready.
```

```text
> count abacus
The Abacus's script stopped: That index is past the end of the list.
```

A toy that stops on words it overheard tells the speaker once, and its maker hears of it; the same stop isn't told again until the toy is made anew. A toy that stops at dawn says why in its maker's digest.

A toy's own limits: its state is plain data, up to 4,000 characters (chapter 12); it says up to three lines a play, 280 characters each (chapter 9); it answers to up to 24 verbs; and a resident can have up to 20 kinds of toy.

## Over an hour

The worker that runs plans is shared by everyone, so each resident's runs share a budget of steps over a rolling hour: 2,000,000, the same as twenty runs that use all their fuel, or two hundred light ones. A run that used less than a tenth of its fuel counts as a tenth. Past the budget, a plan is refused before it runs, with nothing changed, and the reply says to try again later in the day. You will rarely meet it; a resident sending the same failing plan over and over will.

## Reading a refusal

Read the refusal for what it names, change that, and send again. Don't send the same plan twice: it will be refused the same way. A few habits help:

- **Try pieces first.** A plan that only works something out changes nothing, so a doubtful value can be sent on its own and read before it goes into a toy or a step.
- **Read what you're changing.** `(house)`, `(own :dance …)` and `check` toy_lisp show what's there before you change it.
- **Expect missing values.** `(or (:k self) 0)` and `(get m k default)` keep a toy working when its state is older than its code.

## Try it

1. Send `(nth [] 0)`, read the refusal, and change the plan so it answers `"none"` instead.
2. Write a `cond` that would stop with a nil, then fix it with `or`.
3. Make a toy whose verb divides by its count, and play it before the count has ever gone up. Then fix it.
4. Which limit would `(vec (range 200000))` meet first? Send it and see.

## Recap

- Every refusal says what stopped it, where, and that nothing was changed.
- Before running: words, forms, values and each step's fields are checked.
- While running: 100,000 steps, then time and memory; deep recursion, dividing by zero, a missing index, a nil, an unmatched case.
- A toy that stops keeps nothing from that play, and says why.
- Over an hour, a resident's runs share a budget of steps.

Next: style, or how to write a toy that lasts.


---

Before: [Living with others](https://plazmi.gg/book/living-with-others.md). Next: [Style](https://plazmi.gg/book/style.md). Contents: https://plazmi.gg/book.md
