Prism
GuideCookbookExamplesErrorsPlayground

Error index

The compiler catches these at compile time — no partial lessons, no silent zeros. Each one below is verified: the “bad” source actually fails to compile, the “good” source actually compiles clean.

E_UNKNOWN_ID

Unknown identifier

An expression referenced a name that was never declared as `param`/`bool`/`choice` state (or isn't a frame variable like `x`/`t`). Typos get a "did you mean" suggestion.

bad

param t = 0
curve f = t2 { color: primary }

good

param t = 0
curve f = t { color: primary }
E_NONE

"None" is not a value

Older versions silently turned `None` into `Infinity`, producing baffling off-screen geometry. It's a parse error now — omit the prop instead, or bind it to a real boolean.

bad

curve f = x^2 { show: None }

good

bool showF = true
curve f = x^2 { show: showF }
E_DOT

Member access ("a.b") is unsupported

Prism is state-centric, not object-centric — there's no `point.x`. Give each coordinate its own state variable instead.

bad

param p = 0
curve f = p.x

good

param px = 0
curve f = px
E_SCENE_DOMAIN

Scene needs an x domain

`x: [min, max]` is required on every scene (and `y:` too, for `plane`). Without it the renderer has no coordinate system to map to pixels.

bad

scene plane {
  grid
  axes
}

good

scene plane {
  x: [-5, 5]
  y: [-5, 5]
  grid
  axes
}
E_DUPLICATE_EXERCISE

A slide can have at most one exercise

A slide is prose? + scene? + exercise? + goal* — exactly zero or one exercise. Two answerable things on one slide is ambiguous for the Check button. Split into two slides instead.

bad

lesson "L" {
  slide "s" {
    quiz {
      ask "pick one"
      * "a"
      - "b"
    }
    numeric {
      ask "and a number?"
      answer: 1
    }
  }
}

good

lesson "L" {
  slide "s1" {
    quiz {
      ask "pick one"
      * "a"
      - "b"
    }
  }
  slide "s2" {
    numeric {
      ask "and a number?"
      answer: 1
    }
  }
}
E_QUIZ_CORRECT

Quiz needs a correct option

Exactly one option must be marked `*` — the rest use `-`.

bad

quiz {
  ask "pick one"
  - "a"
  - "b"
}

good

quiz {
  ask "pick one"
  - "a"
  * "b"
}
E_UNDEFINED_BIND

Control binds to state that doesn't exist

Every `slider`/`toggle`/`stepper`/`picker`/`button` writes to a `param`/`bool`/`choice` declared earlier in the same scene — declare the state first.

bad

slider t { label: "t" }

good

param t = 0 { range: [-3, 3] }
slider t { label: "t" }
E_HOTSPOT_TARGET

Hotspot needs a target

A `hotspot` exercise needs exactly one `target rect (...)` or `target circle (...)`.

bad

hotspot {
  ask "tap it"
}

good

hotspot {
  ask "tap it"
  target circle (0, 0) { r: 1 }
}
E_TABLE_BLANK

Table needs at least one blank

Every `row:` cell is either a plain given number or a `blank(<answer>)` — at least one cell across the whole table must be a blank, or there's nothing for the learner to fill in.

bad

table {
  ask "fill it in"
  row: 1, 2
}

good

table {
  ask "fill it in"
  row: 1, blank(2)
}
E_MATCH_PAIR

Match pair needs both sides

Each `pair` line is `"left" -> "right"` — both the left and right text are required.

bad

match {
  ask "match them"
  pair "a"
  pair "b" -> "2"
}

good

match {
  ask "match them"
  pair "a" -> "1"
  pair "b" -> "2"
}
E_ONWRONG_TARGET

Detour target must be a hidden slide

An `onwrong:` target has to name a slide in the same lesson that is marked `hidden: true`. Without `hidden`, the scaffold would also appear on the main path, so every learner would see the remediation whether they needed it or not. Unknown ids get a "did you mean" suggestion.

bad

lesson "L" {
  slide "Question" {
    numeric {
      ask "2 + 2?"
      answer: 4
      onwrong: "help"
    }
  }
  slide "Help" {
    id: "help"
    > count on your fingers
  }
}

good

lesson "L" {
  slide "Question" {
    numeric {
      ask "2 + 2?"
      answer: 4
      onwrong: "help" retry
    }
  }
  slide "Help" {
    id: "help"
    hidden: true
    > count on your fingers
  }
}
E_EXPECT_MISMATCH

expect: disagrees with the answer

`expect:` states the derivation of a numeric answer so the compiler can double-check it. If the two don't agree within the exercise's tolerance, the build fails and names both values. That's the point: it catches a mistyped answer before a learner ever sees it. Fix whichever one is actually wrong.

bad

numeric {
  ask "What is 3/8 as a decimal?"
  answer: 0.385
  tolerance: 0.0001
  expect: 3/8
}

good

numeric {
  ask "What is 3/8 as a decimal?"
  answer: 0.375
  tolerance: 0.0001
  expect: 3/8
}
E_SLIDE_ID_REQUIRED

A non-ascii title needs an explicit id

Slide ids are derived from the title by keeping `[a-z0-9]` and nothing else, so an Arabic (or any non-latin) title leaves nothing to build an id from. The compiler would fall back to a positional `slide-2`, which silently changes the moment you reorder slides, and slide ids are a durable key: saved notes, recorded attempts and every `onwrong:` target point at them. A title mixing Arabic with latin math is worse, because it keeps only the scraps, so two different slides can both end up as `f-x`. Write the id yourself.

bad

lesson "النهايات" {
  slide "المشتقة تقيس معدل التغير" {
    > نص
  }
}

good

lesson "النهايات" {
  slide "المشتقة تقيس معدل التغير" {
    id: "derivative-intro"
    > نص
  }
}