Skip to content

Commit 133cd4f

Browse files
dsarnoclaude
andauthored
fix: use update_file() instead of full scan() on single-file writes (#508)
script_create, script_patch, and filesystem write_text each triggered a full recursive EditorInterface.get_resource_filesystem().scan() per write. Under concurrent file creation (e.g. multiple MCP clients, or a stress run) these scans stack: the editor re-enqueues the update_scripts_classes / update_script_paths_documentation WorkerThreadPool tasks while a previous scan is still pending, emitting "Task '...' already exists" / Condition "!tasks.has(p_task)" is true, and the global-class registry is left inconsistent. The next idle filesystem scan can then SIGABRT in ScriptServer::remove_global_class_by_path() — a hard editor crash. Switch all three sites to EditorFileSystem.update_file(path), the single-file registration call the rest of the plugin already uses (material/theme handlers, filesystem reimport, resource_io). It registers just the written file without the recursive scan-action batch, so the duplicate-task race can't occur. Validated with a concurrency stress harness: - before (full scan): 5x "Task ... already exists" + 5x "!tasks.has(p_task)" per run, intermittently escalating to the SIGABRT (3 crash reports). - after (update_file): 0 of those engine errors across heavier runs, no crash. - create -> immediate attach still settles (import_settled=true), so the #261 import-settle behaviour is preserved. - GDScript script (38) + filesystem (16) suites pass; Python import-settle (6) + script/filesystem unit tests (67) pass. Engine-side record of the underlying abort: dsarno/godot#6. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 782397f commit 133cd4f

2 files changed

Lines changed: 21 additions & 5 deletions

File tree

plugin/addons/godot_ai/handlers/filesystem_handler.gd

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,13 @@ func write_file(params: Dictionary) -> Dictionary:
5757
file.store_string(content)
5858
file.close()
5959

60-
# Trigger reimport so the editor recognises the new/changed file
61-
EditorInterface.get_resource_filesystem().scan()
60+
# Single-file register, not a full scan() — a scan() per write stacks
61+
# filesystem WorkerThreadPool tasks under concurrent writes and can SIGABRT
62+
# in the global-class update (see dsarno/godot#6 and create_script in
63+
# script_handler.gd). update_file() is what reimport()/material/theme use.
64+
var efs := EditorInterface.get_resource_filesystem()
65+
if efs != null:
66+
efs.update_file(path)
6267

6368
var data := {
6469
"path": path,

plugin/addons/godot_ai/handlers/script_handler.gd

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,16 @@ func create_script(params: Dictionary) -> Dictionary:
5050
file.store_string(content)
5151
file.close()
5252

53-
# Trigger reimport so the editor recognises the new file
54-
EditorInterface.get_resource_filesystem().scan()
53+
# Register just this file with the editor instead of a full recursive
54+
# scan(). A scan() per write stacks `update_scripts_classes` /
55+
# `update_script_paths_documentation` WorkerThreadPool tasks under concurrent
56+
# script creation ("Task ... already exists" / "!tasks.has(p_task)"), which
57+
# races the global-class registry and can SIGABRT in
58+
# ScriptServer::remove_global_class_by_path (see dsarno/godot#6).
59+
# update_file() is the single-file path the rest of the plugin already uses.
60+
var efs := EditorInterface.get_resource_filesystem()
61+
if efs != null:
62+
efs.update_file(path)
5563

5664
var data := {
5765
"path": path,
@@ -206,7 +214,10 @@ func patch_script(params: Dictionary) -> Dictionary:
206214
write.store_string(new_content)
207215
write.close()
208216

209-
EditorInterface.get_resource_filesystem().scan()
217+
# Single-file register, not a full scan() — see create_script (dsarno/godot#6).
218+
var efs := EditorInterface.get_resource_filesystem()
219+
if efs != null:
220+
efs.update_file(path)
210221

211222
return {
212223
"data": {

0 commit comments

Comments
 (0)