X Tutup
Skip to content

Fix Dictionary::operator[] from C++ accidentally modifying const dictionaries.#106636

Merged
Repiteo merged 1 commit intogodotengine:masterfrom
Ivorforce:dictionary-mutating-fix
Oct 10, 2025
Merged

Fix Dictionary::operator[] from C++ accidentally modifying const dictionaries.#106636
Repiteo merged 1 commit intogodotengine:masterfrom
Ivorforce:dictionary-mutating-fix

Conversation

@Ivorforce
Copy link
Member

@Ivorforce Ivorforce commented May 20, 2025

This fixes a long standing subtle bug where Dictionary can be accidentally modified on const operator[] subscript.

Since #106600 might take a while longer to merge (since it introduces a new type to bikeshed over), I thought it may be appropriate to PR the most important fix separately, so we have time to work through possible regressions.

@Ivorforce Ivorforce added this to the 4.5 milestone May 20, 2025
@Ivorforce Ivorforce requested a review from a team as a code owner May 20, 2025 13:44
@Ivorforce Ivorforce added the bug label May 20, 2025
@Ivorforce Ivorforce requested a review from a team as a code owner May 20, 2025 13:44
@Repiteo Repiteo modified the milestones: 4.5, 4.6 Sep 8, 2025
…dictionaries.

Fix `AudioStreamWav` inserting keys into the input dictionary.
@Ivorforce Ivorforce force-pushed the dictionary-mutating-fix branch from 02b6570 to 0be2a77 Compare September 18, 2025 19:17
return _p->variant_map[key];
static Variant empty;
const Variant *value = _p->variant_map.getptr(key);
ERR_FAIL_COND_V_MSG(!value, empty, "Bug: Dictionary::operator[] used when there was no value for the given key, please report.");
Copy link
Member

@akien-mga akien-mga Oct 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this something that engine users can trigger with their own code, or only something that would be triggered by invalid engine code? Even if only the latter, the please report may lead to people reporting bugs triggered by their own fork or thirdparty modules, so maybe it needs to be phrased differently (maybe just drop please report)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question! I had to look it up. The function is exposed in variant_setget.cpp, which uses getptr and makes its own NULL checks. get() and get_or_add (in variant_call.cpp) likewise use getptr. So i believe the operator[] is only used internally.

@Repiteo Repiteo merged commit d4a87d9 into godotengine:master Oct 10, 2025
20 checks passed
@Repiteo
Copy link
Contributor

Repiteo commented Oct 10, 2025

Thanks!

@Ivorforce Ivorforce deleted the dictionary-mutating-fix branch October 10, 2025 15:42
@ultsi
Copy link
Contributor

ultsi commented Oct 15, 2025

Hey I was running the newest master with my personal project and it kept spamming ERROR: Bug: Dictionary::operator[] used when there was no value for the given key, please report. to me. I git bisected it to point to this commit, funny enough should have looked at when the error message lines were added to save me from hours of compiling.

The error message happens whenever I save a script in an external editor (haven't tested godot editor yet), and then the script is supposedly reloaded (happens in editor with @tool scripts and when saving while in-game too). I get 3-12 error messages at once. What could I do to further help investigate this behaviour?

@Ivorforce
Copy link
Member Author

Ivorforce commented Oct 15, 2025

Hi @ultsi, thanks for tracking this down so far! What we'd need to find out what's causing the problem is a stack trace. I tried reproducing the error message by editing a gdscript file in an external editor, but did not get the same error message as you.
I'm not sure why it's triggering for you but not for me, but if you find out any details, let me know!

Otherwise, if you know how to work with breakpoints, you could also find the stacktrace yourself. It would require adding a breakpoint on dictionary.cpp:136 (the erroring line) with the condition that value == nullptr. That should allow you to get the stacktrace, and report (or fix) the issue.

@ultsi
Copy link
Contributor

ultsi commented Oct 15, 2025

Hey took me a while to learn gdb but managed, so here's the stacktrace @Ivorforce . Don't mind the 137 line instead of 136, added a line to help me set a breakpoint there, so 137 here is the ERR_FAIL_COND_V_MSG call.

(gdb) bt
#0  Dictionary::operator[] (this=0x7fffffffd2f8, p_key=...) at core/variant/dictionary.cpp:137
#1  0x00005555593365ba in LSP::TextDocumentItem::load (this=0x7fffffffd380, p_dict=...) at modules/gdscript/language_server/godot_lsp.h:690
#2  0x000055555933245e in GDScriptTextDocument::load_document_item (this=0x555578ea0a00, p_param=...) at modules/gdscript/language_server/gdscript_text_document.cpp:132
#3  0x0000555559331f9e in GDScriptTextDocument::didSave (this=0x555578ea0a00, p_param=...) at modules/gdscript/language_server/gdscript_text_document.cpp:98
#4  0x000055555932e3ba in call_with_variant_args_helper<GDScriptTextDocument, Variant const&, 0ul>
    (p_instance=0x555578ea0a00, p_method=(void (GDScriptTextDocument::*)(GDScriptTextDocument * const, const Variant &)) 0x555559331f4e <GDScriptTextDocument::didSave(Variant const&)>, p_args=0x7fffffffd5e0, r_error=...) at ./core/variant/binder_common.h:223
#5  0x000055555932cb01 in call_with_variant_args<GDScriptTextDocument, Variant const&>
    (p_instance=0x555578ea0a00, p_method=(void (GDScriptTextDocument::*)(GDScriptTextDocument * const, const Variant &)) 0x555559331f4e <GDScriptTextDocument::didSave(Variant const&)>, p_args=0x7fffffffd5e0, p_argcount=1, r_error=...) at ./core/variant/binder_common.h:337
#6  0x000055555932b12b in CallableCustomMethodPointer<GDScriptTextDocument, void, Variant const&>::call
    (this=0x555578ea12a0, p_arguments=0x7fffffffd5e0, p_argcount=1, r_return_value=..., r_call_error=...) at ./core/object/callable_method_pointer.h:103
#7  0x000055555e014460 in Callable::callp (this=0x555578ea13a8, p_arguments=0x7fffffffd5e0, p_argcount=1, r_return_value=..., r_call_error=...) at core/variant/callable.cpp:57
#8  0x000055555e0147fc in Callable::callv (this=0x555578ea13a8, p_arguments=...) at core/variant/callable.cpp:84
#9  0x0000555559edf7dd in JSONRPC::process_action (this=0x555578ea0388, p_action=..., p_process_arr_elements=true) at modules/jsonrpc/jsonrpc.cpp:128
#10 0x0000555559edfd6e in JSONRPC::process_string (this=0x555578ea0388, p_input=...) at modules/jsonrpc/jsonrpc.cpp:172
#11 0x0000555559317e0a in GDScriptLanguageProtocol::process_message (this=0x555578ea0388, p_text=...) at modules/gdscript/language_server/gdscript_language_protocol.cpp:141
#12 0x000055555931791a in GDScriptLanguageProtocol::LSPeer::handle_data (this=0x55558ac803b0) at modules/gdscript/language_server/gdscript_language_protocol.cpp:96
#13 0x00005555593190a6 in GDScriptLanguageProtocol::poll (this=0x555578ea0388, p_limit_usec=100000) at modules/gdscript/language_server/gdscript_language_protocol.cpp:272
#14 0x000055555932f3e5 in GDScriptLanguageServer::_notification (this=0x555578e9ffa0, p_what=25) at modules/gdscript/language_server/gdscript_language_server.cpp:65
#15 0x0000555559330448 in GDScriptLanguageServer::_notification_forwardv (this=0x555578e9ffa0, p_notification=25) at modules/gdscript/language_server/gdscript_language_server.h:38
#16 0x000055555e423753 in Object::_notification_forward (this=0x555578e9ffa0, p_notification=25) at core/object/object.cpp:993
#17 0x00005555589fe898 in Object::notification (this=0x555578e9ffa0, p_notification=25, p_reversed=false) at ./core/object/object.h:915
#18 0x000055555bc06a8f in SceneTree::_process_group (this=0x555565982d80, p_group=0x555565982fa0, p_physics=false) at scene/main/scene_tree.cpp:1199
#19 0x000055555bc0709e in SceneTree::_process (this=0x555565982d80, p_physics=false) at scene/main/scene_tree.cpp:1279
#20 0x000055555bc04b6c in SceneTree::process (this=0x555565982d80, p_time=0.099998000000000004) at scene/main/scene_tree.cpp:706
#21 0x0000555558af117b in Main::iteration () at main/main.cpp:4748
#22 0x0000555558a068ce in OS_LinuxBSD::run (this=0x7fffffffdf30) at platform/linuxbsd/os_linuxbsd.cpp:991
#23 0x0000555558a1cab0 in main (argc=4, argv=0x7fffffffe568) at platform/linuxbsd/godot_linuxbsd.cpp:85

I also have bt full, do I need it?

@ultsi
Copy link
Contributor

ultsi commented Oct 15, 2025

Hmm it seems that languageId might be missing from vscode lsp plugin. I'm trying a quick fix to see if that's the issue and then will open up a PR

@ultsi
Copy link
Contributor

ultsi commented Oct 15, 2025

@Ivorforce I opened up a PR, have a look #111684

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

X Tutup