diff --git a/scenes/ui/workbench_panel.gd b/scenes/ui/workbench_panel.gd index b7cf4de..0bfe0b5 100644 --- a/scenes/ui/workbench_panel.gd +++ b/scenes/ui/workbench_panel.gd @@ -292,9 +292,10 @@ func _make_bill_row(bill: Bill) -> VBoxContainer: mode_btn.item_selected.connect(func(idx: int) -> void: bill.mode = idx as Bill.Mode Audit.log("workbench_ui", "%s: bill mode → %d" % [current_workbench.label_text, idx]) - # Repopulate so conditional rows update; OptionButton state survives because - # we set mode on bill before the rebuild, and the new row reads bill.mode. - _populate_bills() + # Defer the rebuild — we must NOT free mode_btn while its item_selected + # signal is still emitting (instant crash). call_deferred runs the + # repopulate after the signal frame completes. + call_deferred("_populate_bills") ) mode_row.add_child(mode_btn) @@ -366,7 +367,8 @@ func _make_bill_row(bill: Bill) -> VBoxContainer: current_workbench.label_text, bill.recipe.id if bill.recipe != null else "null" ]) - _populate_bills() + # Defer — same reason as mode_btn: don't free this button mid-emit. + call_deferred("_populate_bills") ) remove_row.add_child(remove_btn)