Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

Editor crashes at shutdown after first discovering a GDExtension class (_gen_extensions_docs runs after cleanup_doc)

Open
#1,544 1 comment 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
55/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
cpp

Research direction

Start in editor_help.cpp at EditorHelp::_gen_extensions_docs() and compare its cleanup handling with load_script_doc_cache(), generate_doc(), and regenerate_script_doc_cache(). Confirm whether the deferred callback can run after cleanup_doc() clears doc, then guard that path and its later doc use. Reproduce with the described headless cold import of a project registering one GDExtension class; done means shutdown exits successfully and generated class help still works.

Written by the indexing model from the issue text.

Description

bug topic:gdextension usability
Tested versions
  • Reproducible in v26.2.stable.official (4f5b14abad) and v26.3.stable.official (85e5203945), official Windows builds.
  • Reproducible in local LLVM-MinGW builds of 85e5203945, including a build that also contains the separate shutdown fixes for #1514 and #1516.
  • Not run on master, but EditorHelp::_gen_extensions_docs() there still has the same unguarded code.
System information

Windows 10 22H2 (build 19045), x86-64. Headless editor (--headless --audio-driver Dummy).

Issue description

The editor can crash at shutdown (0xC0000005) when it discovers a GDExtension that registers a class in a project that has no .godot cache yet. A plain --headless --editor --import of such a project is enough. It does not depend on what the extension does: an extension that registers one empty RefCounted subclass crashes the same way as a full addon, even when the addon's editor plugin is disabled.

This is the same bug as godotengine/godot#111048 and godotengine/godot#111645, which are still open upstream.

Cause. The documentation cache worker queues EditorHelp::_gen_extensions_docs() on the main thread with call_deferred. EditorHelp::cleanup_doc() waits for the worker, then deletes doc and sets it to null. Waiting for the worker does not remove the call it has already queued. A later message-queue flush during shutdown then runs _gen_extensions_docs(), whose first line calls doc->generate(...) on the freed database. The extension XML merge in the same function is a second unguarded use of doc. The other deferred documentation entry points in editor_help.cpp (load_script_doc_cache(), generate_doc() and regenerate_script_doc_cache()) already return early when _cleanup_in_progress is set.

Proposed fix: https://github.com/dominicbytes/redot-engine/pull/4, based on redot-26.3-stable. It makes _gen_extensions_docs() return early once cleanup has started or doc is null:

void EditorHelp::_gen_extensions_docs() {
	// Deferred calls may still run after the documentation has been cleaned up.
	if (_cleanup_in_progress.is_set() || !doc) {
		return;
	}

godotengine/godot#123658 (open) takes a broader approach that ties every deferred documentation callback to an owner object. Redot could take that once it lands in Godot, or use the minimal guard above in the meantime.

Results with the fix. These used headless LLVM-MinGW builds on Windows only. The baseline already contained the #1514 and #1516 fixes, because 26.3-stable has separate shutdown crashes even in an empty project. So the only difference between the two builds is this change.

  • Minimal repro below. The baseline crashed in 5 of 5 runs. The fixed build exited 0 in 5 of 5 runs, with no errors or warnings.
  • Real addon. I used a Steamworks GDExtension that exposes three classes plus compiled class XML, with its own workaround for this crash removed. Its 14-step cold-import matrix covers cold import with the editor plugin disabled and enabled, restart, game run and autoload startup.
    • Baseline: both cold-import steps crashed.
    • Fixed build: every step passed, with debug and with release libraries.
  • Class help. In both a cold and a cached editor session, the help pages for those classes still showed their generated and compiled documentation.

Linux, macOS and the interactive editor still need testing. MSVC results are in the comments below.

Steps to reproduce
  1. Build a GDExtension (godot-cpp is fine) that only registers one class at SCENE level:

    class ReviewProbe : public RefCounted {
    	GDCLASS(ReviewProbe, RefCounted)
    protected:
    	static void _bind_methods() {}
    };
    
    void initialize_probe(ModuleInitializationLevel p_level) {
    	if (p_level == MODULE_INITIALIZATION_LEVEL_SCENE) {
    		ClassDB::register_class<ReviewProbe>();
    	}
    }
    

    The entry function registers that initializer, sets SCENE as the minimum initialization level and returns init().

  2. Create an empty project (config_version=5) and a second project containing the extension DLL and a .gdextension file that points to it. Neither project has a .godot folder.

  3. Using the same editor profile for both (for example a fresh temporary APPDATA), import the empty project first to build the documentation cache, then the extension project:

    <redot editor> --headless --audio-driver Dummy --path <empty project> --quit-after 120 --editor --import
    <redot editor> --headless --audio-driver Dummy --path <extension project> --quit-after 120 --editor --import
    
  4. Expected: both runs exit 0. Actual: the second run crashes with 0xC0000005.

Minimal reproduction project (MRP)

The steps above contain the complete extension source; the projects have no other content.

Dominant language
C++
Stars
6.1k
Forks
337
Avg merge
1d 37m
Merged PRs (30d)
42

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from Redot-Engine/redot-engine

All issues in Redot-Engine/redot-engine

Similar issues

More C++ issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.