Skip to content

When it goes wrong

Find what your script does wrong in the list below, and the fix next to it.

A broken script cannot take AWTRIX down. An error stops only that script: every other app keeps running.

The script stops

ERR: on the display

Any error your script does not handle stops it:

  • the display shows ERR:<name> in red whenever the app's turn comes,
  • the web UI shows the error next to the script and, when the error has a line number, marks and scrolls to that line in the editor,
  • every other script keeps running.

An error found when saving always has a line number. An error while running usually gives the message and the hook it happened in (setup, draw, …), which is normally enough to find it. A script with an error still installs: the source is stored, the app appears in the rotation, and it shows ERR: with the error message.

Fix the line and save again. The script stays stopped until it starts again: when you save a new version, save its settings or data, or restart the device. If the cause of the error is still there, it stops again.

The messages you meet most:

Message Cause Fix
syntax_error: unexpected token … an if, for, while, def or class without its end. The line named can be further down add the missing end
syntax_error: 'clesr' undeclared … a misspelled call correct the name
type_error: 'string' value is not callable a variable named like a built-in call, for example a variable called text before text() give the variable another name, such as label
key_error: … m["key"] on a map without that key read it with m.find("key"), which answers nil
no draw() method the class has no draw() add draw(), or mark a background script @headless true
runtime_error: instruction limit exceeded a call that runs too long see below

instruction limit exceeded

Each call into your script may run at most 2 000 000 instructions. One draw(), one loop(), one button press or one HTTP callback each start again at that limit.

A script that goes over stops with runtime_error: instruction limit exceeded. It starts again when you save a new version, save its settings or data, or restart the device. Nothing else on AWTRIX is affected, and try/except does not catch it.

An endless loop hits this limit, as can a long computation. Split expensive work across timer callbacks to keep the display and other services responsive.

Saving fails

"Not enough memory to compile"

When saving fails with not enough memory to compile, the script was not installed: AWTRIX had too little free memory at that moment. A new script needs about 8 KB of free memory plus its own size. Saving a new version of an installed script needs about 4 KB plus its size.

What helps:

  1. Reboot. This frees up memory in one piece again. Try it before you shorten anything.
  2. Delete a script you do not use. This frees its memory.
  3. Make the script shorter, or use fewer, longer functions. Many one-line helpers need more memory to install than the same code in a few longer methods.

Because a new version needs less free memory than a new script, you can often still edit existing scripts when new ones are refused.

The display shows something else

Text is cut off at the edge

text() never moves. What does not fit is cut off at the edge. Use scroll_text() instead: it stands still when the text fits, and moves it when it does not.

scroll_text("A HEADLINE TOO LONG FOR THE DISPLAY", 0x00AAFF)

Next to an icon, scroll_text(x, y, w, …) moves the text only through its columns. See Text that may not fit.

Text sits in the wrong rows, or does not show

In text() and scroll_text(x, y, w, …), y is the baseline: the line the letters stand on, not their top row. So text(1, 1, "HI 42") shows only the lowest row of the letters, and text(1, 0, …) shows nothing:

class Low
  def draw()
    clear()
    text(1, 1, "HI 42", 0xFFFFFF)
  end
end

return Low()

What the script shows on the display

With the small font, y = 6 puts the capitals on rows 1 to 5. Where text sits lists the y for every font. A second line goes 8 rows lower, for example y = 14.

Moving text stands still

  • Three moving texts. A script moves two texts at a time. A third scroll_text() in the same frame makes all of them start over in every frame, so none of them moves. Keep two at most, and give further lines their own turn, as Two moving texts at once shows.
  • The text fits. scroll_text() moves only text that does not fit. Text that fits stands still in the middle of its columns.

The app leaves in the middle of its text

Your app leaves after its usual time, even when its moving text has not run through. Pass {"repeat": 1} to scroll_text(), and the app stays until the text has run through once. See Keep the app until the text was read.

A value is always 0

Whole numbers divide without a remainder: 50 / 100 is 0, so 50 / 100 * 32 is 0 as well. Multiply before you divide (50 * 32 / 100 is 16), or make one of the numbers a real (50 / 100.0 is 0.5).

Nothing is drawn

Drawing calls work only in draw(). In loop(), setup() or a callback they do nothing and raise no error. Keep the value in a member there, and paint it in draw().

An icon does not show

icon() draws an icon by name only when it is on the clock. Otherwise it returns false and draws nothing. Name the icons in an @icons line and install them with one button, as The icons your script needs shows. A frame takes up to 4 different icons: icon() returns false for a fifth.

The app never shows

  • should_show() returns false. The Apps tab shows the label skipped on its row. See Skip a turn.
  • The script is marked @headless true, so it never draws, or @ondemand, so it waits to be started from the device menu.
  • The app is switched off on the Apps tab.
  • Scripts are switched off under System → Scripting. A banner on the Apps tab says so.

Details

The caps

Every limit a script runs under, such as memory, HTTP, MQTT and timers, and what happens when you reach it is listed under Scripting in Limits.

How many scripts fit depends on the memory set aside for scripts, which all scripts and modules share:

  • A quarter of the available memory at startup, at most 4 MiB.

Device state shows the figure for your device. Keep each script small. Keeping scripts small shows how.

What scripts cannot do

Scripts cannot reach files, the system or other programs' memory. Each script has its own names: its class and top-level variables belong to it alone.

These modules are available:

Module For
string formatting, splitting, searching
json json.load() / json.dump()
math the usual, plus math.rand()
gc gc.collect(), gc.allocated()
strict reports more mistakes when you save, such as a new name without var
global the script's own globals

These are not available, and import raises an error: os, sys, time (use the time functions instead), debug, solidify and introspect. The built-in functions open and input are disabled too.

Scripts don't disturb each other

  • Errors stay inside one script. Each call has its own instruction limit, so one app raising an error or looping forever breaks only itself.
  • Names stay inside one script. App A cannot see app B's class, members or callbacks, and two apps may use the same class name. What an app publishes can be read by all but written only by its owner. What a module returns can be read by every script that imports it.
  • Memory is shared. All scripts use the same memory, so an app that keeps growing its lists takes memory from the others.

These rules protect AWTRIX from accidents. They do not protect you from a script you chose to install: it can publish to any MQTT topic and fetch any URL. Treat a script you did not write like any other program you run.