AssertionError "Document root node was None" on document close: LayerManager.active fallback re-throws

Author: hghallTAZCreated Sep 4, 2026Updated Sep 4, 2026

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 None

Cause

LayerManager.update() already handles this exact state (layer.py:385):

python
root_node = self._doc.rootNode()
if root_node is None:
    return  # Document has been closed

root (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:

diff
     def update_generate_options(self):
-        if not self.model.has_document:
+        if not self.model.has_document or not self.model.document.is_valid:
             return

Applied 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.