Skip to content

Debugging

A Remote Script runs inside Live's embedded Python interpreter, so you cannot just run it and read a traceback in a terminal. This page covers the handful of techniques that make scripting bearable: seeing output, surviving errors, and a checklist for the classic failures.

Log.txt, your console

Live writes a log file you can tail while it runs. Anything your script logs (and every Python exception it raises) lands here.

Locations:

  • macOS, ~/Library/Preferences/Ableton/Live <version>/Log.txt
  • Windows, \Users\<you>\AppData\Roaming\Ableton\Live <version>\Preferences\Log.txt

Watch it live while you work:

# macOS, adjust the version folder
tail -f ~/Library/Preferences/Ableton/Live\ *//Log.txt

To write to it from your script, use the ControlSurface logging helper or Python's logging:

self.log_message("hello from my script")     # simplest, always available
# or
import logging
logging.getLogger(__name__).info("value = %s", value)

self.log_message prefixes the line with your script name, which makes it easy to grep:

tail -f ".../Log.txt" | grep -i "MyScript"

show_message, talk to the user

To surface something in Live's status bar (bottom-left), not just the log:

self.show_message("Mode: SCENE")

Good for confirming a mode switch or a non-fatal condition the performer should see.

Survive your own errors

An unhandled exception in a callback can wedge the script (LEDs freeze, input stops). During development, wrap risky handlers so one bad event doesn't take everything down, and log the traceback:

import traceback

def _on_value(self, value):
    try:
        self._do_something(value)
    except Exception:
        self.log_message(traceback.format_exc())

Remove or tighten these once the logic is stable, silently swallowing exceptions in production hides real bugs.

The reload loop

There is no hot-reload. The cycle is:

  1. Edit the script.
  2. In Live, deselect your control surface in Preferences → Link/Tempo/MIDI, then select it again. (Re-selecting re-runs create_instance.) Some setups require quitting and relaunching Live to clear cached bytecode.
  3. Watch Log.txt for the load and any traceback.

Deleting stale __pycache__ folders next to your .py files avoids running an old compiled version after an edit.

Checklist, "my script doesn't show up"

Work down this list; it covers the overwhelming majority of cases.

  • The folder is in the right place (Live's MIDI Remote Scripts folder, or the user remote scripts folder for your OS) and contains an __init__.py.
  • __init__.py defines create_instance(c_instance) and returns your ControlSurface.
  • The folder name has no spaces or odd characters.
  • There is no import error at load time, check Log.txt; an exception during import makes the script vanish from the dropdown.
  • You re-selected the surface (or relaunched Live) after editing.

Checklist, "it loads but nothing works"

  • Did you build everything inside the component guard? Controls created outside it may not be registered. See the ControlSurface lifecycle.
  • Is the element's MIDI identity (type, channel, number) actually what the hardware sends? Send a message and confirm in Log.txt or with a MIDI monitor.
  • Is the control bound in the currently active layer/mode? A correct component with the wrong layer does nothing.
  • For feedback: did you register a listener on the LOM property, and are you sending the value back out through the element?

Checklist, "LEDs stay lit after I switch it off"

  • Your disconnect() must turn controls off and remove listeners. Leaking listeners is the most common cause of stuck LEDs and of Live getting slower over a session. The lifecycle guide covers clean teardown.