Skip to content

Debug a plugin

Your plugin runs inside the OADM server (its server part) and inside the client (its dialog or page). You debug it by running OADM from your checkout and attaching the debugger to the process that runs the part you want to stop in.

Plugins built in your checkout are copied with their .pdb files to artifacts/plugins/<id>/, and OADM loads them with their symbols, so breakpoints in your code work.

  1. Build: ./manage.sh build plugins (or your IDE’s build).
  2. Start OADM: ./manage.sh run dev starts the server in the background and then the client.
  3. Attach the debugger of Visual Studio, Rider or VS Code (C# Dev Kit):
    • to Oadm.Server for task plugins, queries, core plugins and actions;
    • to Oadm.Client for dialogs, pages and toolbar entries.
  4. Trigger your code: run the task, open the page, fire the rule.

To start the server under the debugger from the beginning (to stop in StartAsync), run the project src/Oadm.Server from your IDE instead of run dev, then start the client with ./manage.sh run client.

OADM reads plugin files into memory when it starts, so a running OADM never locks them: rebuild whenever you like, then restart the server (and the client for client parts) to load the new build.

What Where
Server, started by manage run dev artifacts/logs/server-dev.log
Server log file logs/oadm-server-<date>.log in the data folder (from a checkout: %LOCALAPPDATA%\Oadm, ~/.local/share/Oadm or ~/Library/Application Support/Oadm)
Client log file logs/client-<date>.log in the client’s data folder, and live on the Logs page
One task Details in the tasks pane: the steps and what ctx.Log wrote
Event rules The Event log on the Event rules page: every action run with its result and what ctx.Log wrote

Write to the logs from your plugin:

  • ctx.Logger (server parts) writes to the server log. Use it for what developers and support need.
  • ctx.Log(level, message) in a task writes to that task’s log, which users see. Use it for what users need.
  • Never log a password, a token or a payload with secrets.

Look in the server log first. Every plugin folder writes one of these lines at start:

Loaded plugin package acme.hello 1.0.0 from G:\odam\artifacts\plugins\acme.hello (1 plugin(s))
Plugin problem in G:\odam\artifacts\plugins\acme.hello: Plugin acme.hello requires SDK 0.2.0, host has 0.1.0.
You see Check
No line for your plugin The folder artifacts/plugins/<id>/ exists and holds plugin.json and a *.Server.dll (the build copies them; a build with OadmSkipPluginDeploy=true does not)
“requires SDK …” minSdkVersion in plugin.json is newer than the server’s SDK
“is already loaded from another folder” The same id in two plugin folders: remove one
Loaded, but no menu entry The plugin is off on the Plugins page; your class needs to be public with a public parameterless constructor; ShowInMenus must be true; the user’s role needs the task’s permission
The menu entry is grey CanRun returned false: the tooltip shows your NotSupportedReason
No rail entry for a page The plugin is off, the role lacks “show”, or HasPage is false
“The page of this plugin is not installed on this client.” The client found no ICorePluginPage with your plugin id: check the client part’s PluginId and that its *.Client.dll is in the folder
“The task … needs a client plugin … that is not installed on this computer.” RequiresDialog is true but no ITaskPluginDialog with the same PluginId was loaded: look for “Could not load client plugin” in the client log
“The … page failed: …” CreateView threw: the message and the stack trace are in the client log

Most problems are faster to find in a test than in the running app: a fake camera and a fake context run your task or your service in milliseconds, with breakpoints in your IDE’s test runner. The tutorials show the fakes for each kind of plugin.