What Klipper macros can do

Klipper macro variables: storing and reading state

A macro variable is a value Klipper keeps between macro calls. Declare it, change it, share it and save it, learn when a new value becomes visible, and read position, homing and other printer state in a macro.

Published · Last verified

A Voron toolhead seen from the side, showing its extruder, wiring and cable chain
Photo: disinterpreter, CC BY-SA 2.0

A Klipper macro variable is a variable_<name> line in a [gcode_macro] section. The macro reads it by its bare name, other macros read it as printer["gcode_macro NAME"].name, and SET_GCODE_VARIABLE changes it at run time. Changes last until Klipper restarts. For a value that survives a restart, use SAVE_VARIABLE.

How to declare a variable in a Klipper macro

Add one variable_ line per value above gcode:. Klipper’s configuration reference says the value is “parsed as a Python literal” and “will be available during macro expansion”.

[gcode_macro PARK]
description: Lift the nozzle and move to a park spot
variable_park_x: 10.0      # placeholder: a point inside your bed limits
variable_park_y: 10.0      # placeholder
variable_lift: 10.0        # mm to raise Z
gcode:
    SAVE_GCODE_STATE NAME=park_state
    G91
    G1 Z{lift} F600
    G90
    G1 X{park_x} Y{park_y} F6000
    RESTORE_GCODE_STATE NAME=park_state

Inside PARK, the variables are plain names: {lift}, {park_x}. Any other macro reads them through the printer object:

{% set px = printer["gcode_macro PARK"].park_x %}

Variable names must be lowercase: the documentation says they “may not use upper case characters”. Because the value is a Python literal, the way you write it sets its type:

Type In the config With SET_GCODE_VARIABLE
Integer variable_count: 3 VALUE=3
Float variable_lift: 10.0 VALUE=12.5
String variable_material: "PLA" VALUE='"PETG"'
Boolean variable_done: False VALUE=True
None variable_last_tool: None VALUE=None
List variable_spots: [10, 20] VALUE="[30, 40]"

Strings need quotes, and booleans are capitalized (True, False). An unquoted string such as variable_material: PLA stops Klipper at startup with “is not a valid literal”. A dict works in the config, but not as a VALUE inside a macro body, where { opens a Jinja expression.

How do you change a Klipper macro variable?

Run SET_GCODE_VARIABLE MACRO=<macro> VARIABLE=<name> VALUE=<value>. The variable must already exist as a variable_<name> line in that macro’s section, and Klipper parses VALUE as a Python literal, so strings need quotes inside the value. The change lasts until Klipper restarts; use SAVE_VARIABLE to keep a value across restarts.

# from the console or any macro
SET_GCODE_VARIABLE MACRO=PARK VARIABLE=park_x VALUE=200
# inside a macro called with LIFT=<mm>
SET_GCODE_VARIABLE MACRO=PARK VARIABLE=lift VALUE={params.LIFT|default(10)|float}

MACRO= must match the section name as written, including case, so MACRO=park will not find [gcode_macro PARK].

Klipper evaluates a macro “in entirety and only then” runs the commands it produces. So a macro that sets a variable and then reads it back still sees the old value. Read the new value in a macro that runs afterwards.

Local variables with set

{% set %} creates a local name that lives for one run of the macro and is never stored. A set inside a for loop is also lost when the loop ends: Jinja’s documentation says it is “not possible to set variables inside a block and have them show up outside of it”. A namespace carries the value out:

{% set ns = namespace(total=0) %}
{% for t in [200, 210, 220] %}
  {% set ns.total = ns.total + t %}
{% endfor %}
{action_respond_info("Sum: %d" % ns.total)}

How to make a global variable for Klipper macros

Klipper has no global scope. The usual pattern is a macro that only holds variables, with an empty gcode:. Mainsail’s client.cfg macros read their pause and cancel settings from one called _CLIENT_VARIABLE.

[gcode_macro _GLOBALS]
variable_material: "PLA"
variable_prints_done: 0
gcode:

Read it from any macro as printer["gcode_macro _GLOBALS"].material, with the section name in its exact case. Change it with SET_GCODE_VARIABLE MACRO=_GLOBALS VARIABLE=material VALUE='"ABS"'. The leading underscore keeps the holder off the dashboard: Mainsail’s documentation says any section “with a name starting with an underscore (_) is automatically hidden from the Mainsail interface”.

Can a Klipper macro return a value?

No. A macro expands into G-code commands and has no return statement, so the caller cannot receive a result. The workaround is to store the result in a macro variable with SET_GCODE_VARIABLE and read it in a second macro called afterwards, because Klipper evaluates the whole calling macro before any command it emits runs.

This “function” probes the bed and stores the Z it measured. It needs a [probe] or [bltouch] section.

[gcode_macro MEASURE_Z]
variable_result: 0.0
gcode:
    PROBE
    _MEASURE_Z_STORE

[gcode_macro _MEASURE_Z_STORE]
gcode:
    SET_GCODE_VARIABLE MACRO=MEASURE_Z VARIABLE=result VALUE={printer.probe.last_probe_position.z}

[gcode_macro CHECK_Z]
gcode:
    MEASURE_Z
    _CHECK_Z_REPORT

[gcode_macro _CHECK_Z_REPORT]
gcode:
    {% set z = printer["gcode_macro MEASURE_Z"].result %}
    {action_respond_info("Probe triggered at Z=%.3f" % z)}

Each step needs its own macro. Klipper’s documentation states a called macro “is evaluated when it is invoked”, which is after its caller has been evaluated. _MEASURE_Z_STORE is evaluated once PROBE has finished, so it sees the new reading. _CHECK_Z_REPORT then sees the stored result.

Fixing “Unknown gcode_macro variable”

Klipper raises Unknown gcode_macro variable 'name' when the macro named in MACRO= has no variable called name. SET_GCODE_VARIABLE changes existing variables and cannot create one. Check, in order:

  1. The declaration. Add variable_name: with a starting value to that macro’s section.
  2. The case. Klipper lowercases config option names, so variable_Bed_Temp becomes bed_temp. Write VARIABLE=bed_temp.
  3. The macro. MACRO= must point at the section that declares the variable. A name that matches no macro, including a case mismatch, gives a different error that starts “The value ‘park’ is not valid for MACRO”.
  4. A restart. A variable_ line added to printer.cfg loads only after RESTART.

A typo on the read side raises no error. Jinja treats the missing value as undefined and prints an empty string.

How to read printer objects in a Klipper macro

printer.<section>.<field> reads live printer state. The name after printer is usually a config section, and the Status reference lists every field. Use brackets when a section name contains a space. Each value is a snapshot taken when the macro is evaluated.

[gcode_macro SHOW_STATE]
gcode:
    {% set bed = printer.heater_bed %}
    {% set hotend = printer[printer.toolhead.extruder] %}
    {action_respond_info("Bed %.1f/%.1f C, hotend target %.1f C" % (bed.temperature, bed.target, hotend.target))}
    {% if "temperature_sensor chamber" in printer %}
      {action_respond_info("Chamber %.1f C" % printer["temperature_sensor chamber"].temperature)}
    {% endif %}
    {action_respond_info("X max: %s" % printer.configfile.settings.stepper_x.position_max)}

"name" in printer tests whether an object exists. printer.configfile.settings returns config values as loaded at the last restart. For a chamber heater’s own fields, see chamber heater control.

How to get the current position in a Klipper macro

Read printer.toolhead.position.x (or .y, .z) for the last commanded position in the config file’s coordinates. Read printer.gcode_move.gcode_position.x for the position relative to the G-code origin, the numbers a G1 command would use. From the console, GET_POSITION or M114 prints it.

Both are last commanded values, read before the macro’s own moves run. This macro raises Z by 10 mm without passing the axis limit:

[gcode_macro LIFT_Z]
gcode:
    {% set z = printer.toolhead.position.z %}
    {% set max_z = printer.toolhead.axis_maximum.z %}
    {% set dz = [10, max_z - z]|min %}
    SAVE_GCODE_STATE NAME=lift_state
    G91
    G1 Z{dz} F600
    RESTORE_GCODE_STATE NAME=lift_state

The move is relative because position and axis_maximum use config coordinates, while an absolute G1 would add any SET_GCODE_OFFSET. SAVE_GCODE_STATE also stores the current XYZ position, so RESTORE_GCODE_STATE NAME=lift_state MOVE=1 can return the toolhead to it later.

How to check if the printer is homed in a Klipper macro

printer.toolhead.homed_axes is a string holding the homed axes, such as "xyz". Home only when needed:

[gcode_macro SAFE_PARK]
gcode:
    {% if "xyz" not in printer.toolhead.homed_axes %}
      G28
    {% endif %}
    PARK

To refuse instead, put {action_raise_error("Home the printer first")} inside the if. It aborts the macro and every macro that called it. For a single axis, test "z" in printer.toolhead.homed_axes. Branching is covered on the if/else page.

How to keep a macro variable after a restart

Values set with SET_GCODE_VARIABLE reset to the config value on every restart. To persist one, enable [save_variables] and write it with SAVE_VARIABLE:

[save_variables]
filename: ~/printer_data/config/variables.cfg   # any writable path

[gcode_macro SET_MATERIAL]
gcode:
    {% set m = params.MATERIAL|default("PLA")|string %}
    SAVE_VARIABLE VARIABLE=material VALUE='"{m}"'

Klipper loads stored values into printer.save_variables.variables at startup. As with SET_GCODE_VARIABLE, the name must be lowercase and VALUE is a Python literal. Read it with {% set svv = printer.save_variables.variables %}, then svv.material. Run SET_MATERIAL MATERIAL=PETG to store a new value.

Sources

  1. Klipper documentation, Command templates (accessed October 10, 2026)
  2. Klipper documentation, Configuration reference (accessed October 10, 2026)
  3. Klipper documentation, G-Codes (accessed October 10, 2026)
  4. Klipper documentation, Status reference (accessed October 10, 2026)
  5. Klipper source, klippy/extras/gcode_macro.py (accessed October 10, 2026)
  6. Klipper source, klippy/gcode.py (accessed October 10, 2026)
  7. Mainsail documentation, Hide macros, outputs or fans (accessed October 10, 2026)
  8. Mainsail, mainsail-config client.cfg (accessed October 10, 2026)
  9. Jinja documentation, Template designer documentation: assignments (accessed October 10, 2026)