> For the complete documentation index, see [llms.txt](https://docs.kiloiot.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.kiloiot.io/rules-engine/debugging-rules.md).

# How to Debug a Rule Before You Deploy It

Debug a Kilo IoT rule before deployment: run it step by step with test input, breakpoints, and variable inspection.

A rule that looks correct on the canvas can still behave in ways you didn't expect — a gateway sends the flow down the wrong branch, an expression evaluates against a data shape you didn't anticipate, a variable holds something other than what you assumed. Debug mode lets you find that out *before* the rule is deployed to production, by running the rule step by step and inspecting exactly what happens at every node.

Debug mode is an interactive debugger built into the visual editor. You feed the rule a test payload, then walk through its execution one node at a time — pausing where you want, watching variables change, checking expressions, and deciding how each side effect is handled. It is the difference between deploying a rule and *hoping*, and deploying a rule you have actually watched run.

## How Do I Start a Debug Session?

Before you start, check three things, because the debug buttons only appear when all of them hold:

* **Debug mode is switched on for your organization.** If the top bar shows no **Start Debug** and **Set context** buttons next to **Save**, debug mode is not enabled for your account; ask your administrator or support.
* **The editor is in Editing mode.** The mode selector at the top left of the editor (**Editing**, **Viewing**) must read **Editing**. In **Viewing** the debug buttons are hidden.
* **The rule is saved.** A brand-new rule that has never been saved has no debug buttons; save it first.

1. Open the rule in the [visual editor](/rules-engine/visual-editor.md).
2. In the rule editor's top bar, click **Set context** to open the **Start Debug Session** panel. (The top bar also carries a **Start Debug** button, shortcut **F12**.)
3. The panel asks you to **provide initial context variables** — the input the rule will run against. It opens with one row already added, named `value`, and each row is a **Name** and a **Value**:
   * **Name** is the variable name your rule expects (for example `value`, `temperature`, `status`).
   * **Value** is the test reading. It can be a number, `true` / `false`, `null`, text, or JSON — the panel interprets it for you.
4. Click **Add metric** to add more context variables; remove any extra row you don't need.
5. Click **Load and Start**. The rule loads and pauses, ready for the first step.

The initial context stands in for what a device would send in production. Set it to the values you want to test — the edge case, the threshold, the reading you suspect is causing trouble.

<figure><img src="https://2057878315-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtNQh1wBSHSaknslMdOXm%2Fuploads%2Fgit-blob-da92f454dade56fdc0971a0f07c682e1794c2ab5%2Frules-debug-start-panel.jpg?alt=media" alt="The Start Debug Session panel with a named context variable and the Load and Start button"><figcaption></figcaption></figure>

### How Should I Name the Context Variables?

Rules read the context through `vars`, so name each row to match the expressions in your rule. A gateway condition written `vars.RH > 70` needs a row named **`RH`** — `humidity` or `value` will not do. If a condition references a name the context does not contain, the rule stops at that element when you reach it.

Copy the names out of your gateway conditions rather than typing them from memory, and check the spelling before you start.

Two rows you do not need to add:

* The panel opens with a row named `value`, which suits rules started by **Sensor reading**. A production rule started by **Trigger condition** does not receive `vars.value`, so remove or rename this test value to match the trigger context.
* The debugger provides `sensor_id` automatically. In production it comes from the selected sensor or trigger signal, depending on the Start source.

## Why Does a Debug Session Start Paused?

Loading a session does not run the rule. It opens the diagram, places execution on the Start Event, and waits for you — **nothing runs until you press Run or one of the Step controls**.

When the session loads you will see the canvas turn read-only, the debug toolbar appear at the bottom of the editor, the debug panel open on the right with your initial variables, and a blue outline on the Start Event. The blue outline shows the session is live and waiting.

<figure><img src="https://2057878315-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtNQh1wBSHSaknslMdOXm%2Fuploads%2Fgit-blob-b5e794aa7536c00977b3695c155702528458fd3f%2Frules-debug-session-paused.jpg?alt=media" alt="A loaded debug session paused on the Start Event, with the debug toolbar and the Variables tab"><figcaption></figcaption></figure>

If the diagram appears to sit still, press **Run (F10)** to continue to the first breakpoint or to the end, or **Step over (F9)** to advance one element at a time.

## What Do the Debug Controls Do?

Once a session is loaded, a debug toolbar floats over the bottom of the canvas with five controls. It is not in the header bar next to Save and Build — look at the bottom of the diagram.

* **Run (F10)** — run the rule until it hits a breakpoint or finishes.
* **Step over (F9)** — execute the next node and stop, showing its result.
* **Step into (F8)** — enter the next node and inspect its internals — its inputs, scripts, and outputs — rather than just its result.
* **Run ignore breakpoints (F11)** — run to the end without pausing. It switches every breakpoint off and leaves them off for the rest of the session; re-enable them from the Breakpoints tab when you need them again.
* **Stop (F12)** — end the debug session.

**F12 works at both ends of a session:** press it while editing to start debugging, and again while debugging to stop.

The step controls are available while the session is paused. They are grayed out while the rule is running, while a Side Effect dialog is waiting for an answer, and after a rule failed to load. Stop stays available throughout.

## What Do the Debug Markers on the Canvas Mean?

Elements can carry three different marks during a session:

| Marker                              | Meaning                                                                               |
| ----------------------------------- | ------------------------------------------------------------------------------------- |
| **Blue outline**                    | Where execution is now. The next step runs here.                                      |
| **Small red dot** above the element | A breakpoint. A filled dot is enabled; a hollow ring is one you have switched off.    |
| **Red outline**                     | The element that raised the most recent error. It clears on the next successful step. |

A red outline is neither a breakpoint nor the current position — it marks the element that failed, and the expression on that element is where to look. See [When a node fails](#what-do-i-do-when-a-node-fails-during-debugging).

<figure><img src="https://2057878315-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtNQh1wBSHSaknslMdOXm%2Fuploads%2Fgit-blob-79f2075e33388687f438a37c053d9e2cb05baa38%2Frules-debug-error-marker.jpg?alt=media" alt="A rule paused with a blue outline on the Start Event and a red outline on the element that raised an error"><figcaption></figcaption></figure>

## How Do Breakpoints Work in Rule Debugging?

A breakpoint pauses execution at a specific node, so you can inspect the state at exactly the point you care about.

* **Set a breakpoint** — click a node on the canvas to toggle a breakpoint on it. Open the **Breakpoints** tab in the debug panel to see them all; the tab header shows a count. During a session every click on the canvas adds or removes a breakpoint, so click an element again to clear one you did not intend.
* **Enable or disable** — each breakpoint has a red indicator dot. Click it to switch the breakpoint off without removing it, and on again later.
* **Conditional breakpoints** — create a plain breakpoint first, then click **Set condition** on it and enter a CEL expression. The breakpoint then pauses execution *only* when that expression is true — for example, only when `vars.value > 30`. A conditional breakpoint is marked with a **Conditional** badge. See [CEL Reference](/rules-engine/cel-reference.md) for expression syntax.

Conditional breakpoints are how you debug an intermittent problem: let the rule run normally and stop it only on the reading that triggers the misbehavior.

## How Do I Inspect Variables While Debugging?

The **Variable** tab of the debug panel shows the rule's state at the current step, in two sections:

* **Changes** — the variables that were added or changed since the last step, highlighted so you can see at a glance what the node you just executed actually did. Deleted variables are shown struck through.
* **All variables** — the complete current variable set with their values.

While paused, you can also **edit** the state directly: change a variable's value, add a new variable, or delete one. This lets you push the rule down a branch you want to test without having to restart the session with different input.

When you use **Step into** on a node, the Variable tab also shows an element-detail card with that node's **Inputs**, **Scripts**, and **Outputs** — the internal workings of the step, not just its end result.

## How Do I Watch and Evaluate Expressions While Debugging?

The **Watch** tab keeps an eye on expressions as the rule runs:

* **Add watch** — enter a CEL expression and it is re-evaluated automatically at every step, so you can track a derived value (say, `vars.value - vars.threshold`) without hunting through the variable list.
* **Evaluate** — enter a one-off CEL expression and evaluate it against the current state immediately. Useful for checking a piece of logic — "what would this gateway condition return right now?" — without adding it to the rule.

Both take the same [CEL](/rules-engine/cel-reference.md) you write elsewhere in the rule, and both read the rule's state through `vars` — a watch on a variable named `RH` is written `vars.RH`. An expression that will not compile is rejected as you add it, so use the Watch tab to try a condition out before pasting it into a gateway.

## What Happens to Side Effects During Debugging?

Some nodes do more than transform data — they send notifications, raise alarms, or call out to other systems. When debug execution reaches a node with a side effect, a **Side Effect** dialog appears and asks how to handle it. Three options:

<figure><img src="https://2057878315-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtNQh1wBSHSaknslMdOXm%2Fuploads%2Fgit-blob-23a76ec8501f4e1c77df4138bb3b2c78b0b406e6%2Frules-debug-side-effect.jpg?alt=media" alt="The Side Effect dialog offering Execute, Skip and Mock"><figcaption></figcaption></figure>

* **Execute — run the real handler.** The side effect happens for real, exactly as it would in a deployed rule: an alarm is raised and its recipients are notified, and a command is sent to the physical device.
* **Skip — variables unchanged.** The side effect is skipped and the rule's variables are left as they are.
* **Mock — provide a mock response.** You supply a stand-in response as JSON and the rule continues as if the handler had returned it. The response must be valid JSON — if it isn't, the mock simply isn't applied.

**Execute is selected when the dialog opens**, so change the selection before clicking Apply if you do not want the action to happen for real. This is what lets you debug a rule that sends alerts without actually paging an on-call engineer: choose **Skip** or **Mock** while you're testing the logic, and **Execute** only when you specifically want to verify the real delivery.

Two things to expect once you have answered:

* **Your answer is reused for that node.** If execution reaches the same node again later in the session, the dialog does not reappear and your earlier choice is applied. Choosing **Execute** therefore sends for real on every later pass as well. Start a new session to be asked again.
* **Cancelling puts execution back.** Closing the dialog without choosing returns to just before the node and leaves it unexecuted. Step again and the dialog reappears.

## What Do I Do When a Node Fails During Debugging?

If a node errors during execution, you are told in three places at once, so the red outline is no longer the only clue. The failing node is outlined in red on the canvas, with the message the executor reported shown in a small box under the outline. A banner above the debug panel tabs is titled **Execution failed at "{node}"** (**Execution error** when the failing node cannot be named) and repeats that message; when the error comes from an expression, a monospace **Expression:** line below shows the expression that failed. The same message also appears as a red toast, written **Execution error: {message}**. The message names what actually went wrong, so you can often fix the node without opening anything else.

Not every error means a step failed. A command the debugger refuses, such as a watch expression that does not parse, a breakpoint that does not exist, or too many commands in a short time, is shown as a toast only. It does not draw a red outline or banner, and it leaves the session where it was, so a typo in the **Evaluate** box never looks like your rule broke. A **recoverable** error keeps the debug session paused and loaded — you can inspect the variables, adjust state, and continue — while an **unrecoverable** error ends the session.

Most errors come from an expression on the failing node. Open its properties and check three things:

1. **Every name it uses exists in the Variables tab.** `vars.RH > 70` fails if nothing named `RH` was set as initial context or produced by an earlier node. On a gateway this stops the rule at the gateway — it does not send it down the default branch.
2. **The `vars.` prefix is there.** `RH > 70` is not the same as `vars.RH > 70`.
3. **The expression returns the right type.** A gateway condition must produce `true` or `false`.

Paste the expression into **Evaluate** on the Watch tab to try it against the current state.

## How Long Does a Debug Session Last?

A debug session has a time limit, and stepping through the rule does not extend it. You will see these notifications:

* **Debug session expires in {n} seconds** (warning) shortly before the limit is reached.
* **Debug session timed out** (error) when the time has run out.
* **Debug session closing: {reason}** (warning) when the platform closes the session, with the reason it gives.
* **Debug connection error** (error) when the connection to the debugger is lost.

Start a new session to keep debugging.

Breakpoints are tied to the elements in the diagram you loaded, so reloading the editor can drop them. A warning toast, **{n} breakpoint(s) removed after reload**, tells you how many, so you can re-add them.

If starting a session fails, try again shortly; if it keeps failing, contact support.

## What Are Good Practices for Debugging Rules?

* Debug the edge cases, not the happy path — set the initial context to the threshold value, the missing field, the out-of-range reading.
* Copy variable names out of your gateway conditions when filling in the initial context instead of typing them from memory.
* Use a conditional breakpoint to catch an intermittent problem: run the rule normally and stop only on the reading that triggers it.
* Edit a variable mid-session to force the rule down a specific branch instead of restarting with new input.
* Keep side effects on **Skip** or **Mock** while you iterate on logic; switch to **Execute** only for a deliberate end-to-end check.
* Once a rule debugs cleanly, build and deploy it — see [Builds and Deployment](/rules-engine/builds-artifacts-and-deployment.md).

## Where Can I Learn More About Rules?

* [Visual Editor](/rules-engine/visual-editor.md) — The canvas debug mode runs on
* [CEL Reference](/rules-engine/cel-reference.md) — Expression syntax for conditional breakpoints and watches
* [Builds and Deployment](/rules-engine/builds-artifacts-and-deployment.md) — Ship a rule once it debugs cleanly
* [Troubleshooting](/rules-engine/troubleshooting.md) — Build errors and runtime issues


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.kiloiot.io/rules-engine/debugging-rules.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
