{"record":{"id":"1398c7ce932f2536","repo":"moeru-ai/airi","slug":"edge-group-is-missing-mix-or-map-range-001","errorCode":null,"errorMessage":"Edge group is missing Mix or Map Range.001.","messagePattern":"Edge group is missing Mix or Map Range\\.001\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"engines/stage-tamagotchi-godot/tools/exportXiaoerEdgeReferenceStages.py","lineNumber":161,"sourceCode":"    bangs_node = find_group_node(main_tree, \"刘海阴影\")\n    glow_node = find_group_node(main_tree, \"辉光\")\n\n    def connect_final(socket):\n        link_once(main_tree, socket, output_socket)\n        if viewer is not None and len(viewer.inputs) > 0:\n            link_once(main_tree, socket, viewer.inputs[0])\n\n    def connect_edge_inputs():\n        link_once(main_tree, render_layers.outputs[\"Image\"], edge_node.inputs[\"Image\"])\n        link_once(main_tree, render_layers.outputs[\"Depth\"], edge_node.inputs[\"深度\"])\n        link_once(main_tree, render_layers.outputs[\"法向\"], edge_node.inputs[\"法向\"])\n\n    edge_group = edge_node.node_tree\n    edge_output, edge_output_socket = ensure_group_output_socket(edge_group)\n    mix_node = edge_group.nodes.get(\"Mix\")\n    mask_node = edge_group.nodes.get(\"Map Range.001\")\n    if mix_node is None or mask_node is None:\n        raise RuntimeError(\"Edge group is missing Mix or Map Range.001.\")\n\n    def connect_edge_group_output(socket):\n        link_once(edge_group, socket, edge_output_socket)\n\n    # Raw render layer image.\n    connect_final(render_layers.outputs[\"Image\"])\n    render_path(scene, output_dir / \"reference_raw.png\")\n\n    # Edge node output before bangs shadow and glow.\n    connect_edge_inputs()\n    connect_edge_group_output(mix_node.outputs[\"Result\"])\n    connect_final(edge_node.outputs[0])\n    render_path(scene, output_dir / \"reference_after_edge.png\")\n\n    # Internal edge mask: Map Range.001.Result before Mix.\n    connect_edge_group_output(mask_node.outputs[\"Result\"])\n    connect_final(edge_node.outputs[0])\n    render_path(scene, output_dir / \"reference_edge_mask.png\")","sourceCodeStart":143,"sourceCodeEnd":179,"githubUrl":"https://github.com/moeru-ai/airi/blob/27111382b4a79a7e983289d6e983a06af185ed0f/engines/stage-tamagotchi-godot/tools/exportXiaoerEdgeReferenceStages.py#L143-L179","documentation":"Raised by the Blender export script (exportXiaoerEdgeReferenceStages.py:161) after it locates the compositor group node named '边缘光' (edge light) and inspects its internal node_tree: the group must contain two nodes with the exact Blender names 'Mix' and 'Map Range.001'. If either is missing the script aborts, because the downstream renders (reference_edge_mask.png via mask_node.outputs['Result'], and the edge compositing via mix_node.outputs['Result']) would KeyError on a non-existent node. This is a hard precondition on the source .blend file's internal node graph, not on runtime data.","triggerScenarios":"The '边缘光' group in the .blend file was edited: someone renamed 'Mix' or 'Map Range.001', deleted the Map Range.001 node, or duplicated the group and Blender renumbered the auto-name to 'Map Range.002'; the script is being run against a different/newer .blend file whose edge group layout no longer matches the export tool's expectations; the node was merged into another node and the second Map Range that the script expects no longer exists.","commonSituations":"An artist reworked the edge-light shader and renamed nodes for clarity; a .blend was imported/appended from another file and Blender deduplicated names (applying '.001' suffix to a different node); the export tool was updated to expect 'Map Range.001' but the source file still only has 'Map Range'; running the tool on a stripped/exported .blend where the group internals were collapsed.","solutions":["Open the .blend file in Blender, edit the '边缘光' compositor group, and ensure there is a node literally named 'Mix' and another literally named 'Map Range.001' (rename in the N-panel / Item tab if Blender auto-suffixed them).","If the shader intentionally uses different node names, update the two lookups in exportXiaoerEdgeReferenceStages.py:158-159 (edge_group.nodes.get('Mix') / get('Map Range.001')) to match the real node names, and verify the output sockets ('Result') still exist on those nodes.","Re-create the missing 'Map Range.001' node inside the group and rewire it so the mask render path (mask_node.outputs['Result']) produces the intended edge mask.","Confirm you are running the script against the canonical source .blend the tool was written for, not a re-exported/merged copy."],"exampleFix":"# before: the group was renamed so the lookups return None\nmix_node = edge_group.nodes.get(\"Mix\")         # None\nmask_node = edge_group.nodes.get(\"Map Range.001\") # None\n# -> Edge group is missing Mix or Map Range.001.\n\n# after: align the script with the real node names in the .blend\nmix_node = edge_group.nodes.get(\"Mix.001\") or edge_group.nodes.get(\"Mix\")\nmask_node = edge_group.nodes.get(\"Map Range\")\nif mix_node is None or mask_node is None:\n    raise RuntimeError(\"Edge group is missing Mix or Map Range.\")","handlingStrategy":"validation","validationCode":"# Before relying on the node names, assert the group has the nodes the script needs.\nmix_node = edge_group.nodes.get(\"Mix\")\nmask_node = edge_group.nodes.get(\"Map Range.001\")\nmissing = [n for n, found in ((\"Mix\", mix_node), (\"Map Range.001\", mask_node)) if found is None]\nif missing:\n    raise RuntimeError(f\"Edge group is missing node(s): {', '.join(missing)}. \"\n                       f\"Available nodes: {[n.name for n in edge_group.nodes]}\")","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat the node names 'Mix' and 'Map Range.001' as part of the .blend contract: do not rename them when reworking the '边缘光' group without updating the export script.","When duplicating/importing groups, check Blender's auto-suffix (.001/.002) — it often shifts the expected name onto a different node.","Keep the export script and the canonical source .blend in lockstep: if one changes its node names, update the other in the same commit.","Include the list of available node names in the error message so a mismatch is debuggable without reopening Blender."],"tags":["blender","compositor","node-graph","export-script","godot","reference-stages","asset"],"backgroundTag":null,"analyzedSha":"27111382b4a79a7e983289d6e983a06af185ed0f","analyzedAt":"2026-08-12T18:33:34.132Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}