Practice

Troubleshooting

Every way a bundle fails, listed by what you actually see, with the cause, the fix, and the log line that proves it.

pyNavis fails quietly by design. A bundle the parser cannot use is skipped so that one bad folder never takes the rest of your ribbon with it. That is the right behaviour and it is also why a broken tool looks like nothing at all happened.

The log is the compensation. There is no debugger inside Navisworks, so the runtime writes a reason for every skip to %APPDATA%\pyNavis\logs\pyNavis.log. This page is organised by symptom; each entry ends with the line to search for.

Nothing appeared on the ribbon#

Look at the toast first

After every boot and every Reload, pyNavis counts the bundle folders it had to drop and toasts N bundle folders were skipped, quoting the first one in full and pointing at the log folder for the rest. If you saw that toast, the answer is already on screen and the rest of this section is just the detail. If you did not, the problem is more likely a misspelled .extension, .tab or .panel, or a root that is not registered, since those are the cases the scan cannot see.

Work down this in order. Each question is cheaper to check than the one below it.

Check If no, this is your cause My button did not appear Is the extension root registered? config.json › extensions no pynavis extensions add <dir> yes Are all four suffixes exact? .extension .tab .panel .pushbutton no Typo in a suffix Case is free, spelling is not. No log line. yes Is .extension directly under the root? not one folder deeper no Nesting is not searched Move it up, or register the inner folder. yes Does the bundle hold a script.py? exact file name, in the bundle folder no Button dropped silently No script.py means no button. No log line. yes Stack 2 or 3, pulldown at least 1? counted after bad buttons are dropped no Container skipped, and logged a stack needs 2 or 3 - skipped. yes Did the panel and tab get items? empty panels and tabs are not added no The whole tab disappears One broken button can take the tab with it. yes Did you click Reload? folder changes need a rescan no Click Reload on the pyNavis panel All yes and still nothing: open the log. Every skip the parser makes says why.
The parser drops what it cannot render rather than failing the build, so a missing button is always one of these, in this order.

The extension root is not registered#

Cause. The runtime scans only the folders listed under "extensions" in %APPDATA%\pyNavis\config.json, plus the two defaults, %APPDATA%\pyNavis\extensions and %PROGRAMDATA%\pyNavis\extensions if that folder exists. A perfectly formed extension anywhere else is never seen.

Fix. Register the folder that contains your .extension folder, not the .extension folder itself.

powershell
pynavis extensions add D:\NavisTools
pynavis extensions list

Or edit the file by hand, remembering that JSON needs doubled backslashes:

%APPDATA%\pyNavis\config.json
{
  "extensions": [
    "D:\\NavisTools"
  ]
}

Log. Every root produces a line, whether or not it found anything. A root that is missing from the log was never in the config; a root reporting 0 extension(s) is registered but empty.

text
2026-09-20 09:14:02.418 [INFO ] Extension root 'D:\NavisTools': 0 extension(s).

A folder suffix is misspelled#

Cause. Suffixes are matched case-insensitively but they must be the exact word. Count.pushButton and Count.PUSHBUTTON both work. Count.pushbuttons, Count.push-button and Count.button are invisible.

Fix. Check all four levels: .extension, .tab, .panel, .pushbutton, and .stack or .pulldown where you use them.

Log. A folder inside a .panel whose name contains a dot is taken to be an attempted bundle, so a bad bundle suffix is reported and counted into the N bundle folders were skipped toast after the scan:

text
2026-09-20 09:14:02.427 [ERROR] 'D:\NavisTools\MyTools.extension\MyTools.tab\Selection.panel\Count.pushbuttons' is not a bundle kind pyNavis knows (check the folder suffix) - skipped.

A misspelled .extension, .tab or .panel is the silent case: those three levels are found by globbing for the pattern, so a folder that does not match is never looked at and there is nothing to report.

The .extension folder is nested too deep#

Cause. .extension folders are found directly under a registered root. The scan does not recurse. Putting D:\NavisTools\Team\MyTools.extension under a root of D:\NavisTools finds nothing.

Fix. Move the extension up a level, or register D:\NavisTools\Team as a root of its own. Both are legitimate; registering several roots is normal and they are all scanned.

The pushbutton has no script.py#

Cause. A .pushbutton folder without a file named exactly script.py is not a button and is dropped. A folder holding only config.py, or holding Script.py on a machine where you later copy the folder to a case-sensitive share, hits this.

Fix. Add script.py. There is no way to make a button that only has a Shift+Click action.

Log. Reported, and counted into the skipped-bundles toast. This one cascades: the dropped button may empty a stack, a pulldown, a panel or a whole tab, and only the first line names the real cause.

text
2026-09-20 09:14:02.429 [ERROR] 'D:\NavisTools\MyTools.extension\MyTools.tab\Selection.panel\Count.pushbutton' has no script.py - skipped.

The dockpane equivalent is pane.xaml: a *.dockpane folder without one is skipped, though that skip is logged (Dockpane '<dir>' has no pane.xaml - skipped.), and its script.py is optional.

A stack has the wrong number of buttons#

Cause. A .stack renders as two or three small rows and nothing else. One button, or four, cannot be laid out, so the whole stack is skipped rather than rendered wrong. The count is taken after buttons without a script.py have been dropped, so a stack of three where one is broken becomes a stack of two and still works, and a stack of two where one is broken disappears entirely.

Fix. Two or three .pushbutton folders inside the .stack folder. For anything else use a .pulldown.

text
2026-09-20 09:14:02.431 [ERROR] Stack 'D:\NavisTools\MyTools.extension\MyTools.tab\Selection.panel\01_Pick.stack' holds 4 button(s); a stack needs 2 or 3 - skipped.

A pulldown has no usable pushbuttons#

Cause. A .pulldown with zero valid buttons has nothing to show when opened, so it is skipped. Usually every child is missing its script.py, or the children have the wrong suffix.

Fix. At least one .pushbutton child with a script.py. Unlike stacks there is no upper limit.

text
2026-09-20 09:14:02.436 [ERROR] Pulldown 'D:\NavisTools\MyTools.extension\MyTools.tab\Tools.panel\05_More.pulldown' has no usable pushbuttons - skipped.

The whole tab is missing, not just one button#

Cause. A panel with no items is not added to its tab, and a tab with no panels is not added to the ribbon. Emptiness propagates upward, so one broken button in a one-button extension removes the tab.

Fix. Fix the button. There is no log line for the empty panel or the empty tab, but there will be one for whatever emptied them, or the silent cases above.

Two extensions want the same tab#

Cause. A tab's identity is built from the extension name and the tab folder name. Two copies of the same extension on two registered roots, which is exactly what you get when a development copy and an installed copy are both present, produce the same id. The second one loses.

Fix. Unregister one root, or rename one extension's folder or its name: in extension.yaml.

text
2026-09-20 09:14:03.902 [ERROR] Ribbon tab 'PYNAVIS_TAB_MyTools_MyTools' already exists - skipped (duplicate extension?).

An old loader is still in the Navisworks Plugins folder#

Cause. pyNavis now installs as a per-user Autodesk bundle under %APPDATA%\Autodesk\ApplicationPlugins\pyNavis.bundle. Earlier versions, and old developer deployments, copied the loader into <Navisworks>\Plugins\PyNavis instead. Navisworks loads both locations, so if that folder survives you get two loaders in one process: two boots in the log, duplicate tab ids, and buttons that behave as if something else is fighting them.

Fix. Close Navisworks and delete <Navisworks>\Plugins\PyNavis by hand. That folder is under the Navisworks install directory, so deleting it does need an administrator, which is the last time pyNavis asks anything of one. The setup program warns when it finds the folder; it does not remove it for you, because doing so would need the elevation the rest of the install avoids.

You did not click Reload#

Cause. The ribbon is built from a scan. New folders, renamed folders and new bundle.yaml values are picked up by a rescan, not by the next click.

Fix. Click Reload on the pyNavis panel. It rebuilds without restarting Navisworks and re-activates the tab you were on.

The buttons are in the wrong order#

10 comes before 2#

Cause. Folders are sorted lexicographically, not numerically, so 10_Export sorts before 2_Import. This bites the moment a panel grows past nine tools.

Fix. Zero-pad every prefix to a fixed width, and pad them all the same: 02_, 10_. Leave gaps if you expect to insert tools later.

The number is showing in the title#

Cause. The ordering prefix is stripped only when it is two or more digits followed by an underscore. A single-digit 1_Keep keeps its prefix, and because underscores render as spaces the button reads “1 Keep”.

Fix. Always use two digits. 01_Keep shows as “Keep”. The rule is deliberate, so that a tool genuinely called 1_Thing keeps its name, and a title: in bundle.yaml overrides all of it anyway.

The title or tooltip is wrong#

My edit had no effect#

Cause. Metadata resolves most-specific first and stops at the first hit: bundle.yaml beats the script, which beats the folder name. Adding a __title__ to a script that already has title: in its bundle.yaml changes nothing.

Fix. Edit the winning source, then Reload. Both title and tooltip are read by the parser, so both need a Reload.

A comment ended up in the value#

Cause. The bundle.yaml reader is a deliberately small flat parser. It skips lines that begin with #, but it does not strip a trailing comment. Everything after the first colon, trimmed, is the value.

bundle.yaml
# this whole line is ignored
title: Count selection    # this becomes part of the title

That button is labelled Count selection # this becomes part of the title.

Fix. Put comments on their own line. Quotes are stripped only when they wrap the whole value, so title: "Count selection" is fine but title: "Count" selection keeps its quotes.

__title__ is being ignored#

Cause. The scanner is a regular expression over the file, not a parser. It requires __title__ at the start of a line, at column 0, assigned a simple quoted string on the same line.

python
__title__ = 'Clash report'          # found
__title__ = "Clash report"          # found

    __title__ = 'Clash report'      # indented: not found
__title__ = TITLE                   # not a literal: not found
__title__ = ('Clash '               # spans lines: not found
             'report')

Fix. One line, column 0, a plain quoted string. For anything more complicated use bundle.yaml.

The docstring is not becoming a tooltip#

Cause. A docstring counts only when it is the very first statement in the file. Comments and blank lines above it are skipped, but any actual code before it is not: after an import, the triple-quoted string is just a string.

python
# a comment above the docstring is fine
"""This becomes the tooltip."""
import csv
python
import csv
"""This is a no-op string, not a tooltip."""

There is no tooltip at all#

Cause. Tooltips have no third fallback. Titles fall back to the folder name; tooltips do not fall back to anything.

Fix. Give the script a docstring, or add tooltip: to bundle.yaml. See The bundle.yaml reference for the full precedence table.

The script does nothing#

The main guard is not the cause

Both engines set __name__ to '__main__' before your first statement, so if __name__ == '__main__': is true in a bundle script, a hook and a startup.py alike. If you are debugging a script that does nothing, the guard is not what is stopping it; look further down this section.

The work is in a function nobody calls#

Cause. Defining run() and never calling it produces a script that loads cleanly and does nothing. This is the usual version of "my script does nothing", and it raises nothing and opens no window, so there is no clue on screen.

Do not do this
from pynavis import selection, toast

def main():
    toast.success('%d selected' % len(selection.get_items()))

# nothing calls main()
Do this
from pynavis import selection, toast

toast.success('%d selected' % len(selection.get_items()))

Fix. End the file with the call. If you want to be certain the script is running at all, drop a toast.info('here') at the top; it will tell you in half a second whether the problem is the button or the body.

An empty window opens#

A window appears with almost nothing in it#

Cause. Something called print(). The output window is created lazily on the first write, so a stray print, a debug line, or a library that prints a warning is enough to materialise a whole window for one line of text.

Fix. Use toast for one-line status. Use the output window when you have a report worth a window: a table, a list of problems, a summary with links.

python
from pynavis import toast

toast.success('Exported 412 row(s)', path)     # a corner toast, no window

The distinction is a rule, not a preference. See Talking to the user for which of the four surfaces a given message belongs on.

Errors#

ImportError: could not load the Navisworks .NET API#

text
ImportError: pynavis: could not load the Navisworks .NET API (Autodesk.Navisworks.Api).
This module only works inside a Navisworks session running pyNavis.

Cause. You touched an API-bound module where the assembly cannot resolve. app, doc, selection and clash load the API the moment they are imported; most of the other model-facing domains (sets, viewpoints, viewstate, section, memory, clashtest, export) import cleanly anywhere and raise the same error at the first call that needs the host. Either way it means outside a live Navisworks session, or at import time in a context that does not have one yet.

Fix. Inside Navisworks, nothing is wrong with these imports. Outside it, in a unit test or a build step, do not import them: keep the logic under test in a pure module that has no pynavis import at all, the way clashgroup and viewpoints.plan_renames are built. See Split pure logic from API calls.

When a script must still run in a session that may not have the feature, guard the import and degrade:

python
try:
    from pynavis import clash
except ImportError:
    clash = None

if clash is None:
    forms.alert('Clash Detective is not available in this Navisworks edition.')
else:
    ...

NotSupportedException: Unknown engine#

text
NotSupportedException: Unknown engine 'cpyton'.

Cause. A typo in engine: in bundle.yaml or extension.yaml. The only two valid values are ironpython and cpython.

Fix. Correct the value and Reload.

This one fails at click time

The engine id is not validated during discovery, only when a script is about to run. The button therefore appears, looks completely normal, and throws the moment it is pressed. A button that builds fine and fails on first click is almost always this.

import pynavis fails entirely#

Cause. The runtime could not find the library folder, so nothing on the pynavis namespace is importable by any script.

Fix. Check the "pynavislib" key in config.json, or confirm that a pynavislib folder sits beside PyNavis.Runtime.dll in the runtime directory. This is logged once at startup, not per script, so search the top of the log.

text
2026-09-20 09:14:02.401 [ERROR] pynavislib not found (config 'pynavislib' key or &lt;runtime&gt;\pynavislib) - 'import pynavis' will fail.

import os fails on IronPython#

Cause. IronPython uses the Python standard library that ships with pyNavis. If that folder is missing, every stdlib import fails while pynavis itself, which is loaded from a different path, may still work. The result is confusing: your own imports succeed and import csv does not.

Fix. Reinstall, or point the runtime at a valid stdlib. CPython bundles are unaffected, since they use the interpreter's own library.

text
2026-09-20 09:14:02.399 [ERROR] Python stdlib not found at 'C:\Users\you\AppData\Roaming\pyNavis\2026\runtime\Lib' - 'import os' etc. will fail on IronPython.

A traceback appeared in the output window#

Cause. Your script raised. This is not a bug in the runtime; the engine catches the exception and formats the traceback into the output window on purpose, because there is no console and no debugger to send it to.

Fix. Read the frames from the bottom up. The last frame is your line. If you want the tool to fail politely instead, catch the exception yourself and report it with toast.error(message, detail).

A keyboard shortcut is not working#

The chord does nothing#

Cause. A binding fires only when all four conditions hold: the key went down, it is not an auto-repeat from a held key, the Navisworks main window is the foreground window, and focus is not in a text input.

The last one is stricter than it sounds. When the runtime cannot determine what has focus, it assumes you are typing and does not fire, because stealing a keystroke from a search box is worse than missing a shortcut.

Fix. Click on the model view, then try again. If the chord works there and not elsewhere, focus is the answer.

The chord was rejected as invalid#

Cause. A binding needs Ctrl or Alt unless shortcuts.allowBareKeys is set to true in config.json. Shift alone does not count as a modifier. The key itself must also be in the supported set.

Supported keysNot supported
A to Z Esc, Tab, Space, Enter
0 to 9 Del, Ins, Backspace
F1 to F24 Numpad keys of any kind
Left, Up, Right, Down Punctuation: , . / -
Home, End, PgUp, PgDn Two keys in one chord

Fix. Rewrite the binding. The parser reports the exact reason, so a rejected chord is never a mystery: 'Shift+M': bindings need Ctrl or Alt, 'Ctrl+Esc': unknown token 'Esc', 'Ctrl+A+B': more than one key.

Two tools want the same chord#

Cause. A chord maps to exactly one tool. When two bundles ask for it, the first in ribbon order wins and the second is dropped.

Fix. Change one of them, in bundle.yaml or as a user override. The Shortcuts editor on the pyNavis panel shows conflicts directly, which is faster than reading the log.

text
'pyNavis.tab/Tools.panel/Clash_Report.pushbutton' wants Ctrl+Shift+R but 'pyNavis.tab/General.panel/Reload.pushbutton' already has it - first wins

A config.json override does nothing#

Cause. Overrides are keyed by BundleKey: the bundle folder path relative to its .extension folder, with forward slashes, with every suffix kept and every NN_ prefix kept. It is not the title, and it is not the stripped name.

%APPDATA%\pyNavis\config.json
{
  "shortcuts": {
    "allowBareKeys": false,
    "bindings": {
      "pyNavis.tab/Memory.panel/01_Memorize.pushbutton": "Ctrl+Alt+M",
      "pyNavis.tab/Memory.panel/02_Recall.pushbutton": null
    }
  }
}

null disables a binding the bundle declared. A key that matches no tool is ignored and logged.

text
shortcut override for unknown tool 'pyNavis.tab/Memory.panel/Memorize.pushbutton' ignored

Fix. Alt+Click the button to open its folder in Explorer and read the path off the address bar, or use the Shortcuts editor, which writes the key for you. See Keyboard shortcuts.

The chord runs script.py even though it has Shift in it#

Cause. Working as designed. Keyboard chords always run the primary script. Modifier click actions and keyboard bindings are separate mechanisms that happen to use the same modifier keys, so Ctrl+Shift+K runs script.py, never config.py.

Fix. There is no chord for the secondary action. Shift-click the button.

A dock panel will not open#

The toggle warns that no slot is free#

text
No panel slot free for Clash navigator
Every pyNavis panel slot is claimed. Add more with Panel slots on the pyNavis tab, then restart Navisworks.

Cause. Five dock panel slots ship in the loader, and every *.dockpane bundle claims one for life, recorded in config.json under panes.assignments. A bundle that finds nothing free still renders its ribbon toggle; the toggle just cannot open a panel, and says so when clicked.

Fix. Click Panel slots on the pyNavis tab, enter how many extra slots you want, and restart Navisworks. Slots are Navisworks plugins, and the host scans for plugins only at startup; claiming a freed or new slot afterwards is automatic. Settings and per-tool state covers what the panes section records and why not to edit it.

text
2026-09-20 09:14:03.897 [ERROR] 1 dockpane(s) have no free slot (5 available): Clash navigator
2026-09-20 09:14:03.899 [INFO ] Panes configured: 5 claimed of 5 slot(s).

The toggle says the slot is ready after restart#

text
Panel slot ready after restart
This panel's new slot was just generated. It registers the next time Navisworks restarts, not after Reload.

Cause. Panel slots generated new slots this session and a Reload let your bundle claim one, but the plugin type behind that slot does not exist in the running host yet.

Fix. Restart Navisworks. Nothing else is wrong.

Fewer slots than you asked for#

text
Only 5 panel slots are available
Navisworks did not load the extra slots. Re-run Panel slots, then restart.

Cause. config.json asks for extra slots, but the generated PyNavisPanes.dll did not load, and Navisworks says nothing when it ignores a plugin DLL. The runtime counts the slots that actually registered and trusts that count, so a bundle is never assigned to a slot that does not exist and then silently fail to open.

Fix. Run Panel slots again to regenerate the satellite, then restart Navisworks. The log names the shortfall:

text
2026-09-20 09:14:03.891 [ERROR] Config asks for 8 panel slots but Navisworks registered 5. PyNavisPanes.dll did not load - re-run Panel slots and restart Navisworks. Until then the extra panels are unavailable.

Changing the slot count fails, or reports access denied#

text
Could not add panel slots
The current panel slots are in use, so Navisworks must be restarted before the slot count can change.

Cause. Navisworks maps every plugin DLL into memory at startup and keeps the file mapped for the whole session, so a satellite that loaded can never be deleted or overwritten while Navisworks runs. Windows reports an attempt as ERROR_ACCESS_DENIED, which looks exactly like a permissions problem and is not one: no amount of privilege releases a mapped file. The runtime detects the loaded satellite up front and asks for the restart rather than letting the write fail.

Fix. Restart Navisworks, then set the count. Permissions are almost never the real cause here, because the satellite is written next to the loader in %APPDATA%\Autodesk\ApplicationPlugins\pyNavis.bundle\Contents\<year>, a folder you own. If the write genuinely fails there, something outside pyNavis is holding the folder open, usually a security tool or a sync client.

View > Windows says pyNavis Panel 3, not your title#

Cause. Working as designed. Navisworks builds that menu from the plugin records, which are declared once per slot, so it always lists every slot as pyNavis Panel N, claimed or not. Your bundle's title appears on the ribbon toggle and on the panel's own title bar once it is open.

Fix. Open panels from their ribbon toggles and the naming never matters. An unclaimed slot opened from the menu shows Panel slot N is not in use with a hint to fill it.

The panel opens empty or shows an error card#

Cause. pane.xaml failed to load, its root produced nothing displayable, or script.py raised while wiring the content. A broken panel is deliberately contained: it shows the failure inside its own pane and logs the detail rather than taking Navisworks down during a dock layout restore.

Fix. Read the log, fix the file, and click Reload: open panels are re-contented on every Reload. A script failure also toasts Panel script failed with the bundle title.

text
2026-09-20 10:02:17.204 [ERROR] Dockpane 'Clash navigator': pane.xaml failed to load

A hook or startup.py is not running#

A hook failed three times and went quiet#

text
Hook MyTools:selection-changed disabled after 3 failures
Fix the script, then Reload pyNavis to re-enable it.

Cause. Three consecutive failures disable a hook until the next Reload, so a hook that raises on every selection change cannot fail its way through a whole session. A success resets the count, so an occasional failure never accumulates into a disable. Every failure is logged with its traceback before the third one toasts.

Fix. Search the log for Hook MyTools:selection-changed failed, fix the script, and click Reload, which resets hook health for every hook.

A hook does not fire while a tool is running#

Cause. Working as designed. While a script holds the one-script-at-a-time gate, every event except before-command and after-command skips its hooks rather than running a hook's Python on top of the command's. See Hooks for the full rules, including the debounced events.

text
2026-09-20 10:41:02.118 [INFO ] Hook MyTools:selection-changed skipped: 'Smart Clash Grouper' is running.

startup.py failed#

text
MyTools startup.py failed
See the log; the extension's buttons still loaded.

Cause. The extension's startup.py raised at boot or at Reload. This is deliberately not fatal: discovery carries on and the extension's ribbon still builds, minus whatever the startup script was supposed to prepare.

Fix. The log carries the traceback, labelled [startup MyTools] rather than with the last command clicked. Fix the script and click Reload; startup.py runs again at the start of every Reload.

The icon is wrong#

The button is text only#

Cause. There is no icon.png in the bundle folder. Every variant falls back to icon.png and to nothing else, so a bundle shipping only icon.dark.png has no icon in either theme.

Fix. Ship icon.png first, then add variants. Reload after adding an icon: icons are read by the parser.

FileSizeUsed forFalls back to
icon.png96×96Large buttons, light theme Nothing. Required.
icon.dark.png96×96Large buttons, dark theme icon.png
icon.small.png32×32Stacks and pulldown menus icon.png
icon.small.dark.png32×32Stacks and menus, dark icon.png

The icon is clipped, blurred or enormous#

Cause. Wrong pixel size, or not square. A 32×32 image used as icon.png is scaled up on a large button; a 96×96 image standing in for a missing small variant is scaled down and loses its detail; a non-square image is stretched.

Fix. Produce exactly 96×96 and 32×32 PNGs, both square, with transparent backgrounds. Icons covers the drawing rules and the generator that produces the shipped set. Never hand-edit a shipped icon: change the generator and re-run it.

My edits do not take effect#

Three loops, and the cost of each is very different. Knowing which loop an edit falls into saves more time than any other single fact in this guide.

Restart Navisworks minutes PyNavis.Runtime.dll, config.json paths, *.lib add/remove, panel slots Click Reload one click bundle.yaml, icons, folder names, shortcuts, hooks, startup.py, pynavislib Just press the button again no wait script.py config.py modules beside script.py <extension>\lib\*.py The runtime drops bundle and lib modules from the cache before every run, which is why the inner loop has no step in it at all.
Most authoring happens in the innermost loop. If you find yourself clicking Reload after every code change, you are paying for something you do not need.
What you editedWhat it takesWhy
script.py or config.pyNothing, just run it again The file is read and compiled on every run.
A module beside script.pyNothing, just run it again Bundle modules are dropped from the cache before each run.
A module in <extension>\libNothing, just run it again Same cache eviction as bundle modules.
An existing hook script's contentsNothing, wait for the event Hook files are read from disk each time their event fires.
bundle.yaml or extension.yamlReload Read by the parser at discovery, not at run time.
Any icon*.pngReload Icons are resolved when the ribbon is built.
Folder names, new or deleted bundlesReload The tree is rescanned only on discovery.
Shortcut bindings or overridesReload The keyboard map is rebuilt with the ribbon.
A new hook file, or one disabled by failuresReload The hook list is built at discovery, and Reload resets hook health.
startup.pyReload It runs at boot and at the start of every Reload.
pane.xaml or a dockpane's script.pyReload Open panels are re-contented when the ribbon rebuilds.
A module inside a *.lib folderReload Shared library paths are evicted on Reload, not per run.
The pynavis libraryReload Library modules are not evicted per run, only your own.
config.json extension rootsReload Roots are read when extensions are loaded.
config.json theme or the two ribbon hints Reload Theme and markers are resolved while captions and icons are made.
Adding or removing a whole *.lib folderRestart Navisworks The engine search path is fixed at boot; Reload toasts a reminder.
config.json cpython or pynavislib Restart Navisworks They are baked into the engine configuration at boot, and the CPython interpreter is never shut down. The Settings window says so when you change either.
config.json runtime<year> Restart Navisworks The loader reads it before anything else exists.
The panel slot countRestart Navisworks Slots are plugins, and Navisworks scans for plugins only at startup.
The C# runtimeRestart Navisworks The assembly is loaded into the process once.

A second click is refused#

A toast says something is still running#

text
Clash report is still running
Wait for it to finish, then try again.

Cause. One script runs at a time, process-wide. A long script pumps the message loop so the window can repaint, and a pump dispatches queued input, including the second click you just made. The gate refuses it rather than running two scripts over one document.

Fix. Wait. If a script is hung and never finishes, restart Navisworks: the gate is released when the run ends, and there is no way to cancel a script from outside it. Build cancellation into long tools yourself, the way the shipped grouper and viewpoint managers do.

text
2026-09-20 09:22:41.118 [INFO ] Refused 'Clash report': 'Smart Clash Grouper' is still running.

Where to look#

The logs#

FileWritten byRead it when
%APPDATA%\pyNavis\logs\pyNavis.logThe runtime Anything after the plugin has loaded: discovery, ribbon, shortcuts, script runs.
%APPDATA%\pyNavis\logs\loader.logThe loader plugin pyNavis is not there at all: no tab, no Reload button, nothing.

A healthy startup looks like this. Every line is one you can search for, and the count lines are the ones that matter most.

%APPDATA%\pyNavis\logs\pyNavis.log
2026-09-20 09:14:02.372 [INFO ] ==== pyNavis runtime booting ====
2026-09-20 09:14:02.380 [INFO ] Runtime assembly: C:\Users\you\AppData\Roaming\pyNavis\2026\runtime\PyNavis.Runtime.dll
2026-09-20 09:14:02.391 [INFO ] Navisworks API assembly: 23.0.1400.86
2026-09-20 09:14:02.418 [INFO ] Extension root 'D:\NavisTools': 1 extension(s).
2026-09-20 09:14:02.427 [INFO ] Extension root 'C:\Users\you\AppData\Roaming\pyNavis\extensions': 1 extension(s).
2026-09-20 09:14:02.430 [INFO ] Boot sequence complete (ribbon build may be deferred until ribbon exists).
2026-09-20 09:14:03.884 [INFO ] Shortcuts: 5 binding(s) active.
2026-09-20 09:14:03.896 [INFO ] Running startup.py for 'MyTools'.
2026-09-20 09:14:03.902 [INFO ] Panes configured: 1 claimed of 5 slot(s).
2026-09-20 09:14:03.951 [INFO ] Ribbon built: 2 tab(s), 27 button(s).
2026-09-20 09:14:03.958 [INFO ] Hooks configured: 1 (MyTools:doc-opened).

If Ribbon built reports fewer buttons than you expect, the difference is accounted for above it. If Shortcuts reports fewer bindings than you declared, the rejected ones are named on the lines around it. If Panes configured claims fewer panels than you have, the overflow line above it names the bundles left out. Clicking Reload writes ==== pyNavis reload ==== and repeats the whole sequence, so the newest run is always at the bottom.

Alt+Click to open the folder#

Alt+Click any pyNavis button to open its bundle folder in Explorer with script.py already selected. It is the fastest way to confirm which file a button is actually running, which matters as soon as two copies of an extension exist on disk.

The Console#

The Console on the pyNavis panel is an interactive Python prompt inside the live session, with the same environment your scripts get. It is the right tool for the questions that are hard to answer by reading code: what type is this, what does that property actually return, how many items are really selected.

python
from pynavis import doc, selection
items = selection.get_items()
len(items)
doc.get_property(items[0], 'Item', 'Type').ToDisplayString()
[c.DisplayName for c in items[0].PropertyCategories]

Run a snippet with the Run button or Ctrl+Enter; Up and Down at the edges of the input recall history. Its namespace survives between runs within one window, so you can build up state across several snippets rather than rewriting the setup each time. Printed text and expression values echo straight into the transcript, and anything you render through pynavis.output opens the console's own output window, titled for the console, rather than appending to whichever tool ran last.