Step Debugging Lucee with Extension Debugger

edit Introduced: 6.2

Step Debugging Lucee with Extension Debugger

Extension Debugger (formerly LuceeDebug) is a Debug Adapter Protocol (DAP) debugger for Lucee CFML, originally created by David Rogers and now maintained under the Lucee organisation.

It lets you set breakpoints, step through code, inspect variables, and evaluate expressions — all from VS Code or any DAP-compatible IDE.

There are two ways to run it:

Lucee Extension Java Agent
Lucee version 7.1+ 6.2+
Setup Install extension, set env vars JVM startup flags
JDK required No (JRE works) Yes
Overhead Zero when not attached Bytecode instrumentation at class load

If you're on Lucee 7.1 or later, use the extension. It's simpler, faster, and supports more features.

Extension Mode (Lucee 7.1+)

The extension hooks into Lucee's native debug instrumentation. When no debugger is attached, the hooks are JIT-compiled away — zero overhead in production.

Install

Install the extension using one of:

  • Lucee Admin — search for "debugger" in the extensions page
  • Environment variableLUCEE_EXTENSIONS=org.lucee:debugger-extension
  • Manual deploy — download the .lex from Maven Central and drop it in your deploy folder

Configure

Set environment variables and restart Lucee:

LUCEE_DAP_SECRET=your-secret-here
LUCEE_DAP_PORT=10000

Variable Required Description
LUCEE_DAP_SECRET yes Authentication secret (must match client config)
LUCEE_DAP_PORT Port for DAP server (default: 10000)
LUCEE_DAP_HOST Bind address (default: localhost, use 0.0.0.0 for Docker)
LUCEE_DAP_BREAKPOINT Set to false to disable breakpoint instrumentation
LUCEE_DEBUGGER_DEBUG Set to true for verbose debug logging

Docker

For Docker, bind to 0.0.0.0 and expose the DAP port:

services:
  lucee:
    image: lucee/lucee:7.1
    environment:
      LUCEE_DAP_SECRET: my-secret
      LUCEE_DAP_PORT: 10000
      LUCEE_DAP_HOST: 0.0.0.0
      LUCEE_EXTENSIONS: org.lucee:debugger-extension
    ports:
      - "8888:8888"
      - "10000:10000"

See the Docker example in the extension repo for a complete working setup.

Java Agent Mode (Lucee 6.2+)

For older Lucee versions, the debugger runs as a Java agent that instruments bytecode at runtime via JDWP. This requires a full JDK (not JRE).

Download the agent JAR from Maven Central — click the version, browse, and download debugger-agent-{version}.jar. For example:

https://repo1.maven.org/maven2/org/lucee/debugger-agent/3.0.0.4/debugger-agent-3.0.0.4.jar

See the Java Agent setup guide for detailed instructions.

VS Code Setup

  1. Install the luceedebug extension from the VS Code Marketplace

  2. Add a debug configuration to .vscode/launch.json:

{
    "type": "cfml",
    "request": "attach",
    "name": "Lucee Debugger",
    "hostName": "localhost",
    "port": 10000,
    "secret": "your-secret-here"
}

  1. Press F5 or click "Start Debugging"

Launch Options

Option Description
hostName DAP server host (default: localhost)
port DAP server port (must match LUCEE_DAP_PORT)
secret Authentication secret (must match LUCEE_DAP_SECRET)
logLevel Log verbosity: error, info, debug, trace (default: info)
consoleOutput Stream console output to debug console (extension mode only, default: true)
logExceptions Log exception stacktraces to the debug console (default: true)
pathTransforms Map IDE paths to server paths (see below)

Path Transforms

When your IDE sees files at different paths than Lucee does (e.g. Docker, remote servers), use pathTransforms to map between them:

"pathTransforms": [
  {
    "idePrefix": "/Users/dev/myproject",
    "serverPrefix": "/var/www"
  }
]

A breakpoint set on /Users/dev/myproject/Application.cfc maps to /var/www/Application.cfc on the server. Multiple transforms can be specified — first match wins.

Features

Feature Extension Agent
Line breakpoints yes yes
Conditional breakpoints yes yes
Function breakpoints yes no
Exception breakpoints yes no
Step in/out/over yes yes
Variable inspection yes yes
Set variable value yes no
Watch expressions yes yes
Debug console evaluation yes yes
Hover evaluation yes yes
Completions (autocomplete) yes no
Console output streaming yes no
breakpoint() BIF yes no
isDebuggerEnabled() BIF yes no

CFML BIFs (Lucee 7.1+)

Note: These BIFs are not yet listed in the auto-generated function reference as the docs currently build against Lucee 7.0.

These BIFs are part of Lucee core — they are always available and safe to call. Without the debugger extension installed and active, they simply return false and do nothing. You can leave them in production code without any overhead or dependency concerns.

breakpoint()

Programmatic breakpoint — like JavaScript's debugger; statement. Suspends execution when a debugger is attached, allowing inspection of variables.

// simple breakpoint
breakpoint();

// labelled breakpoint — shows in the debugger UI breakpoint( label="before query" );
// conditional — only breaks when the condition is true breakpoint( condition=( arrayLen( errors ) > 0 ) );

Returns true if the breakpoint was hit, false if skipped (no debugger attached or condition was false).

isDebuggerEnabled()

Returns true if DAP debugger support is enabled (via LUCEE_DAP_SECRET env var or lucee.dap.secret system property). Useful for conditionally including debug logic:

if ( isDebuggerEnabled() ) {
    systemOutput( "Debug: processing #arrayLen( items )# items" );
}

Troubleshooting

Breakpoints Not Binding

Use the command palette and run "luceedebug: show class and breakpoint info" to inspect what's happening.

Conditional Breakpoints

  • Conditions that fail (not convertible to boolean, or throw an exception) evaluate to false
  • Watch out for x = 42 (assignment) vs x == 42 (equality check)

Links

See also