AssertionError "Document root node was None" on document close: LayerManager.active fallback re-throws
Summary
Closing a document while the AI docker is open raises AssertionError: Document root node was None out of GenerationWidget.update_generate_options.
A closed Krita document keeps its Python wrapper alive, but activeNode() and rootNode() both return None. Model.has_document only tests that the wrapper exists, so the guard in update_generate_options passes. LayerManager.active then catches the first AttributeError and falls back to LayerManager.root, which asserts on the same dead document. That second exception escapes the slot.
Traceback (1.53.0)
Traceback (most recent call last):
File "...\ai_diffusion\layer.py", line 445, in active
layer = self.find(self._doc.activeNode().uniqueId())
AttributeError: 'NoneType' object has no attribute 'uniqueId'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "...\ai_diffusion\\\ui\generation.py", line 1004, in update_generate_options
has_active_region = regions.is_linked(self.model.layers.active)
File "...\ai_diffusion\layer.py", line 457, in active
return self.root
File "...\ai_diffusion\layer.py", line 438, in root
root = ensure(self._doc.rootNode(), "Document root node was None")
File "...\ai_diffusion\\\util.py", line 100, in ensure
assert value is not None, msg or "a value is required"
AssertionError: Document root node was NoneCause
LayerManager.update() already handles this exact state (layer.py:385):
root_node = self._doc.rootNode()
if root_node is None:
return # Document has been closedroot (layer.py:436) and active (layer.py:442) do not. So the except in active falls back into a property that raises for the same reason the original call did.
update_generate_options is connected to layers.active_changed (ui/generation.py:843), and that signal fires during document teardown.
Fix
KritaDocument.is_valid (document.py:295) tests precisely this condition, and is already used for it in model/root.py:59, model/root.py:160, and document.py:353. ui/generation.py:993 uses the weaker check:
def update_generate_options(self):
- if not self.model.has_document:
+ if not self.model.has_document or not self.model.document.is_valid:
returnApplied locally: the traceback stops and the docker survives closing a document.
Same pattern elsewhere
ui/region.py:314 _update_actions is bound to the same layers.active_changed signal (region.py:242) and reads self._root.layers.active with no validity guard, so it can reach the same fallback.
A broader fix would be to stop LayerManager.active's error path from routing through a property that can itself raise.
Environment
- Plugin 1.53.0
- Krita 5.2.11 (git a4da714), Windows 11, embedded Python 3.10.7
client.log holds 61 Error getting active layer: 'NoneType' object has no attribute 'uniqueId' entries. Most are caught harmlessly; only the fully-closed-document case escapes as an AssertionError.
Source: Acly/krita-ai-diffusion