Debugging

Finch includes a built-in browser debugging bar for local development. When enabled, a collapsible panel appears at the bottom of every rendered page, giving you real-time insight into the current request without leaving the browser.

The debug bar shows:

  • Registered routes and which one matched the current request
  • Request details (method, path, headers, session, cookies)
  • Template parameters passed to the current view
  • Server logs and errors for the current request
  • Buttons to reload the page or restart the server

Enabling the Debugger

Two conditions must both be true for the debugger to activate:

  1. enableLocalDebugger: true in FinchConfigs — this allows the debugger to run at all.
  2. isLocalDebug returns true — this indicates the app is currently running in development mode.

isLocalDebug is computed automatically:

  • If the environment variable LOCAL_DEBUG is set, it uses that value (true/false).
  • Otherwise it checks Console.isDebug, which is true when the app is run with dart run (not compiled with dart compile exe).

In app.dart:

FinchConfigs configs = FinchConfigs(
  // Allow the debugger to be shown when running in debug mode
  enableLocalDebugger: env.getBool('ENABLE_LOCAL_DEBUGGER', false),
);

In your .env file (development):

ENABLE_LOCAL_DEBUGGER=true
LOCAL_DEBUG=true

In production, both should be false (or simply omit them, as the defaults are false).

How It Works

When enableLocalDebugger && isLocalDebug, Finch:

  1. Injects debug data into every renderView() response.
  2. Appends the debug bar HTML at the bottom of the page.
  3. Exposes a WebSocket-based live-reload endpoint.

The debug bar is never injected when:

  • enableLocalDebugger is false
  • isLocalDebug is false
  • The response is JSON (renderData()) or plain text (renderString())

Checking Debug Mode in Code

You can read isLocalDebug in your controller or template to conditionally show development-only content:

// In a controller
if (FinchApp.config.isLocalDebug) {
  Console.p('Debug: current user is ${rq.getSession("user")}');
}
<!-- In a template -->
{% if isLocalDebug %}
  <p class="debug-note">Running in debug mode</p>
{% endif %}

isLocalDebug is automatically injected as a template variable for every renderView() call, so no rq.addParam() is needed.

Terminal Panel

When the app is started with finch serve (not a plain dart run), the CLI opens a WebSocket server on --terminalPort (default 8282) that streams the running process's stdout/stderr. The debug bar's Terminal tab connects to this port automatically, so you can watch server logs and console output without switching to the terminal window, and send commands back to the running process from the browser.

# Use a custom terminal port
finch serve --terminalPort 9000

This only works through finch serve, since that's the process that spawns the app and wires FINCH_TERMINAL_PORT into its environment. Running the app directly with dart run leaves the Terminal tab disconnected.

DartDevTools Panel

finch serve also starts the app with --enable-vm-service=8181/0.0.0.0 --disable-service-auth-codes, binding the Dart VM service to all interfaces (not just localhost) so it stays reachable from inside a Docker container. The debug bar embeds a DevTools tab that connects to this VM service on port 8181, giving you memory, performance and logging views for the running app directly in the browser.

If you're running Finch in Docker, make sure ports 8181 and 8282 are published alongside the app port — see Docker for Finch.

Error Pages

In debug mode, error pages (e.g., 404, 500) include a full stack trace to help diagnose the issue. In production mode, error pages show only a user-friendly message without internal details.