Both the XSLT Debugger and DotLiquid Debugger let you step through a template and inspect variables. But they work differently under the hood — and those differences affect what you can do while debugging.
The Fundamental Difference
XSLT debugging is live. The XSLT Debugger supports two engines, each with its own instrumentation strategy:
-
Saxon (XSLT 2.0/3.0) — exposes a
TraceListenerinterface withEnterandLeavecallbacks that fire as each instruction executes. The engine cooperates natively. -
.NET
XslCompiledTransform(XSLT 1.0) — has no TraceListener, so the debugger rewrites the stylesheet at load time, injecting<dbg:probe>extension calls into everytemplate,if,for-each, andwhenblock. A registered extension object handles each probe and pauses execution on aTaskCompletionSourceuntil you click Step.
Both approaches genuinely pause execution. You're inspecting a running process.
Liquid debugging is replay. DotLiquid has no such API — Template.Render() runs the whole template and returns. The extension records a trace during that render, then lets you step through the recording. By the time you click Step, the template has already finished.
Capability Comparison
| XSLT Debugger | DotLiquid Debugger | |
|---|---|---|
| Step model | Live pause/resume | Trace replay |
| Breakpoints | Yes — set and hit mid-execution | No |
| Backward stepping | No | Always — it's just a recording |
| Variable state | Live, from the running engine | Recorded snapshot at each step |
| Modify and continue | No | No — edit and re-render |
| Conditional breakpoints | No | No |
| Filter chain tracing | N/A | Yes — each filter is a step |
| Branch visibility | Taken branch only | Taken branch only |
| Re-render cost | Steps are free (engine is paused) | One render upfront, steps are free after |
What This Looks Like in Practice
Setting a breakpoint:
In the XSLT debugger you can set a breakpoint on a specific <xsl:template> or <xsl:for-each>, hit F5, and the debugger stops there — even if that template fires 50 iterations in. You never see the first 49.
In the DotLiquid debugger there are no breakpoints. You start at step 1 and click forward. For a template with many iterations you can drag the step slider to jump ahead quickly, but you can't say "stop when item.qty > 10".
Backward stepping:
The DotLiquid debugger supports backward stepping — you're just moving a cursor through a recording. The XSLT Debugger does not support step back.
Modifying state:
Neither debugger supports modify-and-continue. In both cases you edit the template or input and re-render from scratch.
Filter chain tracing:
This is where the DotLiquid debugger has an advantage. Because every filter application is recorded as a separate step, you can step through name | Upcase | Truncate: 10 | Append: "…" and see the value after each filter. XSLT doesn't have filter chains — XPath functions are composed inline and there's no equivalent granularity.
Why the Difference Exists
Both XSLT engines provide a path to genuine pause/resume — either through a native TraceListener (Saxon) or through stylesheet rewriting at load time (.NET XSLT 1.0). The key is that XSLT execution is structured: templates fire, instructions execute in sequence, and there are clear entry/exit points to hook into.
DotLiquid was designed as a simple, safe rendering library. It has no extension points for interrupting execution, and its render loop is a single synchronous call with no observable mid-execution state. The replay approach is the only option available without forking the engine.
Which Should You Use?
If you're debugging XSLT maps — especially complex structural transformations, apply-templates logic, or recursive templates — the live debugger is significantly more powerful. Breakpoints and live state make it practical to debug templates that are hundreds of lines long. Use the compiled engine for XSLT 1.0 (including inline C#); use Saxon for XSLT 2.0/3.0. The full series is covered in XSLT Debugging in Logic Apps.
If you're debugging Liquid maps — filter results, conditional branches, loop variable values — the replay model covers the common cases well. The main limitation is the absence of breakpoints; for most Liquid templates this is a minor inconvenience rather than a real blocker. For a deeper look at Liquid templates and the debugger extension, see DotLiquid Debugging in Logic Apps.
Top comments (0)