Skip to content

Debugging

David Benson edited this page Oct 7, 2026 · 1 revision

This page shows how to debug draw.io: running the unminified sources, the URL parameters and console helpers that help, getting at the running editor from the browser console, and the usual causes of stale code, CORS errors and silent embed failures. It assumes you can serve the web app locally as described in Getting started. For collecting a console log as an end user, see Open the JavaScript console.

Development mode (?dev=1)

To debug the sources, open index.html?dev=1 on your local server. js/bootstrap.js then loads the source files instead of the bundles:

Without dev=1 With dev=1
Editor code js/app.min.js (between PreConfig.js and PostConfig.js on hosts other than draw.io), then shapes-14-6-5.min.js, stencils.min.js and extensions.min.js js/PreConfig.js, js/diagramly/Init.js, js/grapheditor/Init.js, mxgraph/src/mxClient.js (which writes one <script> per graph library file), then js/diagramly/Devel.js (grapheditor, diagramly, sidebars, importers) and js/PostConfig.js
Script loading Deferred <script> elements document.write, so the files run in order
Version in the Help menu The release number v@DRAWIO-VERSION@ (the build replaces this placeholder)
Service worker On draw.io hosts, or with offline=1 or enableSW=1 Only with offline=1 or enableSW=1; on draw.io hosts an installed one is removed
Content-Security-Policy From the server The development policy from Devel.js as a <meta> tag
Error reports to draw.io Only from draw.io hosts Never

In development mode every file appears under its own name in the Sources panel of the browser's developer tools, so breakpoints, stack traces and "Go to definition" work. Some code is still minified even then: the bundles built outside this repository (js/elk/, js/mermaid/, js/plantuml/, js/vsd/, js/libavoid-js/libavoid.min.js), the vendored libraries (DOMPurify, pako, ...) and, in the public repository, js/stencils.min.js. A few features load extra bundles on demand; the Network panel shows whether a feature runs from a source file or from a bundle.

Without dev=1 you run the bundles, which only change when you rebuild them (Building). A quick check of what you are running is the version at the bottom of the Help menu.

URL parameters for debugging

These are the parameters that matter while debugging. The complete list for users is at Supported URL parameters; the configuration is described in Configuration.

Parameter Effect Code
dev=1 Loads the sources (see above) bootstrap.js
test=1 Turns on EditorUi.debug output in the console, including the startup line App.start with the EditorUi instance and the startup time, every incoming embed message (with proto=json), and extra checks in the storage and sync code. Like app.diagrams.net, it also picks the simple theme on small or touch screens when no theme is set; add ui=kennedy to avoid that EditorUi.debug, App.main
dev-tooltips=1 With dev=1: cell tooltips show the ID, geometry, points, absolute points and style of the cell DevTools.js
print-csp=1 With dev=1: prints the Content-Security-Policy variants to the console Devel.js
local=1 Device storage only; no cloud backends and no access to localStorage, so settings are neither read nor saved and every load starts from the defaults bootstrap.js, diagramly/Init.js
plugins=0 Loads no plugins App.main
math=0 Does not load MathJax App.main
splash=0 Skips the splash screen
ui=kennedy The classic theme with a menu bar, so menu paths are stable regardless of window size App.main
offline=1 Disables the cloud backends and plugins, and allows the service worker on any host diagramly/Init.js, Editor.enableServiceWorker
offline=0 Unregisters the service worker and shows "Cache cleared" (on hosts other than draw.io only together with enableSW=1) App.main
enableSW=1 / pwa=0 Allows the service worker on any host / leaves the service worker alone Editor.enableServiceWorker
sync=none|manual|auto Collaboration mode (DrawioFile.SYNC) DrawioFile.js
stealth=1, lockdown=1 No requests to services other than the storage diagramly/Init.js
chrome=0, lightbox=1 Read-only viewer modes, to reproduce viewer problems in the editor code App.js
cors=<regex> URL-encoded regular expression of URLs that send CORS headers; matching URLs are fetched directly instead of through the proxy Editor.prototype.isCorsEnabledForUrl
embed=1&proto=json Embed mode with the JSON protocol EditorUi.prototype.installMessageHandler

On app.diagrams.net and in the desktop app, Shift+click on the version number in the Help menu toggles the same debug output as test=1 (the about action in diagramly/Menus.js). On other hosts the click does nothing; add test=1 to the URL instead.

The running editor in the console

draw.io keeps no global reference to the editor. The supported way to get one is the plugin entry point, which also works from the console once the app has started (unless plugins=0 or offline=1 is set):

Draw.loadPlugin(function(ui) { window.ui = ui; });

var graph = ui.editor.graph;

With test=1, the App.start line in the console contains the EditorUi instance as well; in Chrome you can right-click it and choose "Store as global variable". In an embedded editor, first pick the iframe in the context selector of the console.

Useful calls once you have ui:

Call Returns
graph.getSelectionCell(), graph.getSelectionCells() The selected cells
graph.model.getCell(id) A cell by ID
graph.model.getStyle(cell), graph.getCellStyle(cell) The style string, and the resolved style object
graph.view.getState(cell) The view state: bounds on screen, absolutePoints of edges, the shape and text objects
mxUtils.getPrettyXml(ui.editor.getGraphXml()) The current page as indented XML
ui.getFileData(true) The whole file (<mxfile>, all pages)
ui.currentPage, ui.pages, ui.getCurrentFile() Pages and the open file
Graph.decompress(text) The XML inside a compressed <diagram> element
ui.editor.setGraphXml(mxUtils.parseXml(xml).documentElement) Loads a model into the current page

The classes and the graph model are described in Architecture and Graph model.

Inspecting a diagram

  • Extras > Edit Diagram (in the Simple and Minimal themes: Insert > Advanced > Edit Diagram; action editDiagram) shows the current page as indented, uncompressed XML. Edit it and apply with Ctrl+Enter (a whole <mxfile> pasted there replaces the file), or copy it into a bug report. EditDiagramDialog is in grapheditor/Dialogs.js.
  • Edit Style (Ctrl+E / Cmd+E) shows the raw style string of the selected cell, Edit Data (Ctrl+M / Cmd+M) its ID and custom properties.
  • Compressed files store each page as Base64 of the raw-deflated, URI-encoded XML (Graph.compress); Graph.decompress reverses it. The format is described in File format.

Logging

Helper Use
EditorUi.debug(...) Diagnostic output with a timestamp, printed only with test=1. On iOS it writes to mxLog instead. Use it for messages you want to keep in the code.
EditorUi.logError(message, url, line, column, err) Unexpected errors. Writes to console.error and, on draw.io hosts only, reports the error to draw.io.
mxLog.show(), mxLog.debug(...) A floating console window inside the page, for environments without developer tools. mxLog.TRACE = true enables the timing output of mxLog.enter(name) / mxLog.leave(name, t0).
console.log Temporary output. In the code base it is guarded with if (window.console != null).

Uncaught errors during startup are caught by the window.onerror handler of App.main, which prints message, URL, line and column to the console and then shows an alert "Error: ...". After startup, App.prototype.start installs a handler that shows them in an error dialog. Before App.main runs, the handler in bootstrap.js writes "Page could not be loaded. Please try refreshing." into the splash screen.

Stale code

When your change does not show up, one of these is usually serving old code:

  • Bundles instead of sources. Without dev=1 the editor runs js/app.min.js and the other bundles. Rebuild them or use development mode. The public build compiles mxgraph/mxClient.js as it is and does not regenerate it from mxgraph/src, so changes to the graph library only appear in development mode (see Coding conventions). The viewer, the lightbox and embedded diagrams run viewer.min.js or viewer-static.min.js, which need their own rebuild.
  • The browser cache. Simple static servers often send no cache headers, and browsers then cache scripts heuristically, so a reload can run the old file. Tick "Disable cache" in the Network panel while the developer tools are open, or serve the files with Cache-Control: no-store. To be sure that an edit is live, check a function in the console, for example Graph.prototype.getLinkForCell.toString().
  • The service worker. Editor.enableServiceWorker registers a service worker (which precaches the app) only on *.diagrams.net and *.draw.io, or anywhere with offline=1 or enableSW=1, and never with pwa=0. If one is registered on your host, open clear.html (unregisters every service worker of the origin) or remove it in the Application panel of the developer tools. For app.diagrams.net, see Clear the draw.io cache.
  • Configuration. PreConfig.js and PostConfig.js are loaded in development mode and on every host other than the draw.io domains. Values set there (such as urlParams['sync'] = 'manual' and window.EXPORT_URL = null in the public PreConfig.js) apply to your tests as well.

CORS, the proxy and fonts

The editor fetches remote files, images and fonts from the browser, so the remote server must allow it with CORS headers. Editor.prototype.isCorsEnabledForUrl treats these URLs as fetchable directly: same origin, raw.githubusercontent.com, icons.diagrams.net, Google Fonts, and anything matching the cors URL parameter. Everything else goes through PROXY_URL (by default proxy next to the editor, served by ProxyServlet):

Operation Code Uses the proxy for other URLs
Opening a diagram from a URL (#U..., ?url=) App.prototype.loadTemplate Yes
Embedding remote images in exports Editor.prototype.createImageUrlConverter Yes
Embedding fonts in exports Editor.prototype.mapFontUrl Yes
Loading fonts from http: URLs (avoids mixed content) Graph.addFont Always

On a self-hosted installation the proxy answers 404 unless ENABLE_DRAWIO_PROXY=1 is set for the Java server, and a static host has no proxy at all. The symptoms are a failed open of a remote diagram, or images and fonts that are visible in the editor but missing in PNG and PDF exports. Fixes, in order of preference: serve the files from the editor's origin, add CORS headers to the server that hosts them (and list it in cors if needed), or enable the proxy (see Self-hosting and Security).

Custom fonts are only loaded from URLs that pass Graph.isValidFontUrl (http:, https:, data: or relative). Google Fonts URLs become <link> elements, all others @font-face rules (Graph.createFontElement in diagramly/Editor.js).

Embed mode

To see the messages between your page and an embedded editor:

  • From the editor to your page: log everything that arrives in your page.

    window.addEventListener('message', function(evt)
    {
    	console.log('from editor', evt.origin, evt.data);
    });
  • From your page to the editor: add test=1 to the iframe URL. With proto=json the editor then logs each message it accepts (EditorUi.prototype.installMessageHandler) in the iframe's console.

Messages that the editor drops without any error:

  • Messages from a window other than its parent or opener (evt.source check).
  • With proto=json, anything that is not a JSON string. Post JSON.stringify(msg), not the object; a parse failure is ignored.
  • Messages sent before the editor has said it is ready (its listener is installed just before it says so). With proto=json it sends {"event":"init"}; without it, the string ready (or the value of the ready URL parameter).

The protocol itself is documented at Embed mode and in Embedding.

Performance

  • Rendering. With fastRendering (on by default, Fast Rendering in the Configuration dialog) the editor paints cells once in model units under one transform, so zoom and pan do not repaint the diagram (mxGraphView.prototype.modelCoordinates). Turn it off in the configuration to check whether a problem depends on it. The lightbox and the viewer always render this way. See configure the diagram editor.
  • Profiling. Record with the Performance panel of the developer tools. Development mode gives readable function names, but it loads around 300 separate scripts, so measure startup and absolute timings with the bundles. test=1 logs the startup time in the App.start line.
  • Narrowing down. Rule out optional parts with math=0, plugins=0 and local=1, and compare a copy of the diagram with parts removed through Edit Diagram.

Common problems

Symptom Cause Fix
An edit to a source file has no effect Running the bundles, the browser cache, or a service worker ?dev=1, disable the cache, clear.html (see Stale code)
Help shows v@DRAWIO-VERSION@ Development mode, where the version placeholder is not replaced Expected
Changes in mxgraph/src are missing after a build The build uses the prebuilt mxgraph/mxClient.js Test in development mode
"Page could not be loaded. Please try refreshing." on the splash screen A script failed before the app started, often a 404 Check the console and the Network panel
An alert "Error: ..." during startup, or an error dialog later Uncaught exception Console stack trace; reproduce with ?dev=1 for readable names
Console shows Blocked plugin: or Unknown plugin: Plugin on another origin, or not built in while ALLOW_CUSTOM_PLUGINS is false See Plugins and Security
Remote diagram does not open, or images and fonts are missing in exports on your own host No CORS headers and no proxy See CORS, the proxy and fonts
Shift+click on the version enables nothing Only works on app.diagrams.net and in the desktop app Use ?test=1
Embedded editor ignores your messages Object instead of JSON string, sent before init, or wrong window See Embed mode
Settings are forgotten after a reload local=1, or local storage blocked by the browser Remove local=1; allow site data
PDF export opens the print dialog on your own host window.EXPORT_URL = null in the public PreConfig.js See Self-hosting

See also

Clone this wiki locally