Sign in to see your models

Save models to your account and pick up where you left off on any device.

Sign-in is temporarily unavailable. Please try again later.

Documentation

Diagnostics cookbook

Ten errors the compiler reports, each with a broken snippet, the exact message, the cause, and the fix. Find the message you got below.

Reference

Diagnostics

The two base models

Every entry below starts from one of two small working models and breaks one thing. The broken and fixed snippets show only the sections that change; the rest of the model stays as written here. Each compiler message is copied verbatim from compiling the broken variant.

Base model A is a patch-only fire spread. Base model B is a pet model: grazing sheep with one action.

base model A — fire (patches)

world:  x: -20..20  y: -20..20  topology: box params:  density: number = slider(62, 0..100) patches:  state: empty | tree | burning | burned = empty setup:  patches:    state := if (random-float(100) < density) then tree else empty     where (tree and px = min-x):      state := burning step staged:  patches:    where burning:      state := burned     where (tree and any neighbors4 where burning):      state := burning stop when (count patches where burning = 0)

base model B — grazing (pets)

world:  x: -20..20  y: -20..20  topology: torus patches:  has-grass: boolean = true pets:  sheep:    energy: number = 0    actions:      graze:        energy := energy + 1        patch.has-grass := false setup:  create sheep 30  sheep:    set-random-position step:  sheep:    forward(1)    where (patch.has-grass):      graze    where (energy < 0):      die

1. A declaration section after a lifecycle section

In base model A, params: was moved between setup: and step staged:.

broken

setup:  patches:    state := if (random-float(100) < density) then tree else empty     where (tree and px = min-x):      state := burning params:  density: number = slider(62, 0..100) step staged:  patches:    where burning:      state := burned

compiler output

Agent updates use `agentset:` or `agentset where (...):` followed by indented actions. Statement: `params:`
Declarations in `params:` use `name: type = value`. Statement: `density: number = slider(62, 0..100)`

Declaration sections (world:, params:, monitors:, patches:, pets:, …) must all come before the lifecycle sections (setup:, step:, stop when). Once the parser is inside a lifecycle section, a bare params: line can only be read as an agent-update header, so the message talks about agentsets rather than ordering. If a diagnostic complains about an agentset on a line where you wrote a section header, check the section order first.

fixed

params:  density: number = slider(62, 0..100) setup:  patches:    state := if (random-float(100) < density) then tree else empty     where (tree and px = min-x):      state := burning step staged:  patches:    where burning:      state := burned

2. An agent block not indented under its step

In base model B, the sheep: block header lost its indentation under step:.

broken

step:sheep:    forward(1)    where (patch.has-grass):      graze    where (energy < 0):      die

compiler output

Lines inside `step:` must be indented. Statement: `sheep:`.

Sections own their bodies through indentation. A line at column zero is a new top-level section header, and sheep is not a section name, so the parser reports the un-indented line instead. Indent the whole agent block two spaces so it belongs to the step.

fixed

step:  sheep:    forward(1)    where (patch.has-grass):      graze    where (energy < 0):      die

3. stop when without parentheses

In base model A, the parentheses around the stop condition were dropped.

broken

stop when count patches where burning = 0

compiler output

Assignments in `setup:` and `step:` use `:=`, not `=`. For example: `patches:` followed by an indented `state := tree`. Statement: `stop when count patches where burning = 0`

The stop condition must be in parentheses: stop when (...). Without them the grammar rejects the whole line, and because the line contains a bare =, the message guesses you meant an assignment, which is misleading here. If this message points at a stop when line, the fix is the parentheses, not the operator.

fixed

stop when (count patches where burning = 0)

4. Calling patch-here() instead of the patch property

In base model B, the sheep step tests the patch underfoot with the old function form.

broken

step:  sheep:    forward(1)    where (patch-here().has-grass):      graze

compiler output

`patch-here()` was renamed — use the `patch` property (e.g. `patch.has-grass`).

patch is a property of the current pet, like x, y, and heading, not a function call. The old patch-here()form was removed because the parentheses were misleading: it took no arguments and returned a writable reference. Older MathPets sources and habits from NetLogo's patch-here still produce it, so the compiler keeps a targeted message.

fixed

step:  sheep:    forward(1)    where (patch.has-grass):      graze

5. Calling the removed kill-one command

In base model B, the sheep step removes a sheep on the same patch with the old bundled predation helper.

broken

step:  sheep:    forward(1)    where (energy < 0):      die    kill-one(sheep, energy, 5)

compiler output

kill-one was removed — write `let prey = one-of breed here` followed by `where (prey): kill(prey)`.

kill-one hid three decisions in one call: which agent to pick, whether one was there at all, and what the killer gains. The replacement spells out each one: one-of picks, where (prey): handles the empty-patch case, and the energy gain is a plain assignment you can change on its own.

fixed

step:  sheep:    forward(1)    where (energy < 0):      die    let prey = one-of sheep here    where (prey):      kill(prey)      energy := energy + 5

6. A zero-parameter action declared with parentheses

In base model B, the graze action was declared with an empty parameter list.

broken

pets:  sheep:    energy: number = 0    actions:      graze():        energy := energy + 1        patch.has-grass := false

compiler output

Zero-parameter action "graze" must be declared without parentheses.

The rule across the language is parentheses if and only if the call has arguments. graze takes none, so it is declared and called bare, like the built-in commands die and set-random-position. The opposite mistake is caught too: calling an action that takes parameters without parentheses reports Parameterized action "graze" must be called with parentheses.

fixed

pets:  sheep:    energy: number = 0    actions:      graze:        energy := energy + 1        patch.has-grass := false

7. Calling an action the breed does not define

In base model B, the step calls munch but the action is declared as graze.

broken

step:  sheep:    forward(1)    where (patch.has-grass):      munch

compiler output

Unknown pet action "munch" for agentset "sheep".

A bare identifier used as a statement inside a pet block is read as an action call. So this message covers more than typos: it also appears when you call an action declared on a different breed, or when a field name stands alone on a line. Check the actions: block of the breed named in the message; the action must be declared there or in the shared pets:-level actions: block.

fixed

step:  sheep:    forward(1)    where (patch.has-grass):      graze

8. Movement commands inside step staged

In base model B, the sheep step was declared step staged: while still calling forward(1) and die.

broken

step staged:  sheep:    forward(1)    where (patch.has-grass):      graze    where (energy < 0):      die

compiler output

Staged steps do not support pet commands. Use a non-staged step for movement.

step staged: is the two-phase mode: every read sees the state from before the section, and writes commit together at the end, which is what cellular-automaton rules need. Pet commands such as forward, hatch, and die are not supported there. Keep patch rules staged and put movement in a plain step:; a model can declare both, and they run in source order each tick.

fixed

step staged:  patches:    where (not has-grass and random-float(100) < 3):      has-grass := true step:  sheep:    forward(1)    where (patch.has-grass):      graze    where (energy < 0):      die

9. self outside an agent context

In base model B, a monitor tries to read a field through self.

broken

monitors:  my-energy: number = self.energy

compiler output

self is not valid in observer contexts.

Monitors, observer-level setup statements, and stop when run as the observer, so there is no current agent for self to refer to. self is only valid inside an agent update block or an action body. At the observer level, aggregate over the agentset instead.

fixed

monitors:  mean-energy: number = mean sheep report (energy)

10. Testing a lookup against undefined

In base model B, the grass check was written the way a JavaScript author would guard a possibly-missing value.

broken

step:  sheep:    forward(1)    where (patch.has-grass != undefined):      graze

compiler output

there is no `undefined` in Pets — test the lookup itself: `where (not pet-at(...))`.

MathPets has no undefined or null; the value for a missing agent is nobody. A declared field always holds a value of its type, so test it directly. A lookup that can miss, such as one-of sheep here or pet-at(...), is false when it finds nothing, so guard it by testing the result itself in a where clause.

fixed

step:  sheep:    forward(1)    where (patch.has-grass):      graze     # guarding a lookup that can miss:    let prey = one-of sheep here    where (prey):      kill(prey)