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#
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.
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.
pynavis extensions add D:\NavisTools
pynavis extensions listOr edit the file by hand, remembering that JSON needs doubled backslashes:
{
"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.
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:
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.
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.
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.
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.
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.
# this whole line is ignored
title: Count selection # this becomes part of the titleThat 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.
__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.
# a comment above the docstring is fine
"""This becomes the tooltip."""
import csvimport 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#
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.
from pynavis import selection, toast
def main():
toast.success('%d selected' % len(selection.get_items()))
# nothing calls main()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.
from pynavis import toast
toast.success('Exported 412 row(s)', path) # a corner toast, no windowThe 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#
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:
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#
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.
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.
2026-09-20 09:14:02.401 [ERROR] pynavislib not found (config 'pynavislib' key or <runtime>\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.
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 keys | Not 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.
'pyNavis.tab/Tools.panel/Clash_Report.pushbutton' wants Ctrl+Shift+R but 'pyNavis.tab/General.panel/Reload.pushbutton' already has it - first winsA 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.
{
"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.
shortcut override for unknown tool 'pyNavis.tab/Memory.panel/Memorize.pushbutton' ignoredFix. 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#
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.
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#
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#
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:
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#
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.
2026-09-20 10:02:17.204 [ERROR] Dockpane 'Clash navigator': pane.xaml failed to loadA hook or startup.py is not running#
A hook failed three times and went quiet#
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.
2026-09-20 10:41:02.118 [INFO ] Hook MyTools:selection-changed skipped: 'Smart Clash Grouper' is running.startup.py failed#
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.
| File | Size | Used for | Falls back to |
|---|---|---|---|
icon.png | 96×96 | Large buttons, light theme | Nothing. Required. |
icon.dark.png | 96×96 | Large buttons, dark theme | icon.png |
icon.small.png | 32×32 | Stacks and pulldown menus | icon.png |
icon.small.dark.png | 32×32 | Stacks 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.
| What you edited | What it takes | Why |
|---|---|---|
script.py or config.py | Nothing, just run it again | The file is read and compiled on every run. |
A module beside script.py | Nothing, just run it again | Bundle modules are dropped from the cache before each run. |
A module in <extension>\lib | Nothing, just run it again | Same cache eviction as bundle modules. |
| An existing hook script's contents | Nothing, wait for the event | Hook files are read from disk each time their event fires. |
bundle.yaml or extension.yaml | Reload | Read by the parser at discovery, not at run time. |
Any icon*.png | Reload | Icons are resolved when the ribbon is built. |
| Folder names, new or deleted bundles | Reload | The tree is rescanned only on discovery. |
| Shortcut bindings or overrides | Reload | The keyboard map is rebuilt with the ribbon. |
| A new hook file, or one disabled by failures | Reload | The hook list is built at discovery, and Reload resets hook health. |
startup.py | Reload | It runs at boot and at the start of every Reload. |
pane.xaml or a dockpane's script.py | Reload | Open panels are re-contented when the ribbon rebuilds. |
A module inside a *.lib folder | Reload | Shared library paths are evicted on Reload, not per run. |
The pynavis library | Reload | Library modules are not evicted per run, only your own. |
config.json extension roots | Reload | 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 folder | Restart 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 count | Restart Navisworks | Slots are plugins, and Navisworks scans for plugins only at startup. |
| The C# runtime | Restart Navisworks | The assembly is loaded into the process once. |
A second click is refused#
A toast says something is still running#
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.
2026-09-20 09:22:41.118 [INFO ] Refused 'Clash report': 'Smart Clash Grouper' is still running.Where to look#
The logs#
| File | Written by | Read it when |
|---|---|---|
%APPDATA%\pyNavis\logs\pyNavis.log | The runtime | Anything after the plugin has loaded: discovery, ribbon, shortcuts, script runs. |
%APPDATA%\pyNavis\logs\loader.log | The 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.
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.
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.