godot
godot copied to clipboard
GDScript "Cannot find member" errors for user-defined classes when member definitely exists, only resolved by reloading project in the editor
Tested versions
This is occurring for me with v4.2.2.stable.mono.official [15073afe3]
System information
Windows 10
Issue description
This has now been happening for me both when writing code in the editor, and when writing code in another IDE using the editor's LSP. When adding new members to one script, then adding a reference to those new members in another script, I will often receive a "Cannot find member" or "The method is not present" error that I have only been able to resolve by restarting the editor and reloading the project. The editor mysteriously and intermittently neglects to register the new member and acknowledge that it can be accessed without an error until after the project is reloaded. Making further changes and re-saving the script with the added members does not help.
Steps to reproduce
This occurs intermittently and I am not able to give reproduction steps.
This is one GDScript file in my project where recently this has been happening especially frequently (please do not judge my very WIP code):
https://files.pineapplemachine.com/public/logs/MonsterTerrain3D.2024.08.12.gd
Minimal reproduction project (MRP)
The issue is intermittent and the cause is not clear to me.
Edit 2024-09-02: I've been having this problem (as described in the original issue submission) with other code files that don't have a preload like described below, and are not related to the code files that do. If anything, it is starting to seem like maybe the problems simply begin to happen once a source file is long enough, somewhere around 300 to 500 lines? Although the comment below is still notable for describing another additional situation where this problem is happening, I don't think this is strictly related to the self-preload thing.
After continuing to experience this problem I have realized that it must be related to my use of the workaround described here to refer to the class of self in type annotations, i.e. at the top of some of the GDScript files in my project I have written const ThisClass := preload('ThisClass.gd').
https://github.com/godotengine/godot-proposals/issues/391#issuecomment-1854710017
I have noticed that I have not ever experienced this missing member/method problem when working in the same file where it's defined, only when referring to another class (whether by preload for a non-global class, or referencing a global class that uses class_name without the use of preload) - except in the case that I am referring to a member or method of a value typed as ThisClass in the source file that uses the aforementioned workaround.
Is there any better way to refer to the class of self in type annotations that won't cause this kind of breakage? Having to constantly reload the project as I make script changes is time-consuming and annoying, But having no way to refer to the self type in annotations would force me to turn off GDScript type safety errors that are otherwise very useful for catching my mistakes.
Here's the specific code and error where I realized this was the likely culprit:
I have a class named MonsterTerrain3DSegment. Here are lines from the top of the script:
@tool
extends Node3D
# https://github.com/godotengine/godot-proposals/issues/391#issuecomment-1854710017
const MonsterTerrain3DSegment := preload("MonsterTerrain3DSegment.gd")
Here is a method of this class which returns another MonsterTerrain3DSegment:
func get_segment_at_offset(segment_coord_offset: Vector2i) -> MonsterTerrain3DSegment:
if not self.has_terrain():
return null
var segment_coord := segment_coord_offset + self.terrain_coord
return self.terrain.get_segment_at(segment_coord)
Here is another method which depends on the value returned by get_segment_at_offset. I have encountered an error like described in the original issue description. I can only get rid of this error by reloading the project in the editor. The error occurs on the last line, segment.notify_geometry_invalidated(), where the type of segment is MonsterTerrain3DSegment.
func notify_neighbors_geometry_invalidated() -> void:
for x: int in range(-1, 2):
for y: int in range(-1, 2):
if x == 0 and y == 0: continue
var coord := Vector2i(x, y)
var segment := self.get_segment_at_offset(coord)
if is_instance_valid(segment):
segment.notify_geometry_invalidated()
I have noticed that I have not ever experienced this missing member/method problem when working in the same file where it's defined, only when referring to another class (whether by preload for a non-global class, or referencing a global class that uses class_name without the use of preload) - except in the case that I am referring to a member or method of a value typed as ThisClass in the source file that uses the aforementioned workaround.
I get this buggy behaviour without the workaround you mentioned. My method is simply to use class_name; the behaviour had appeared earlier, then disappeared, then it's back again.
However, I was able to make the suppress the bug by deleting scripts (on a duplicate test project, of course). However, when I remove a class, I would obviously get script parsing errors (bad ref) on scripts that would reference it. When this happens, I think it ends up not being "registered" in the session/memory, and somehow the bug disappears.
I have to qualify this, however. When I delete a class that is relied on heavily, more scripts break.
- I deleted a subsets of scripts, got parsing errors, and the bug went away.
- I incrementally re-instated these scripts again. I would fewer parsing errors, but the bug was still not exhibiting.
- Then I would re-instate a particular script that made the bug appear again.
- What I noticed was that the script I re-instated removed much of the parsing errors, i.e. most of my dependent scripts were parsed correctly.
From that observation, I will tentatively go off on the idea that there is some limit that is being reached.
I have about 40 classes right now.
I'm continuing to investigate this.
(Note to self: reduce class_name)
Another observation:
- Let's say I have 2 scripts that serve as statics:
StaticsConvo.gdandConstants.gd - If I add a member or method in
Constants.gdthe Godot lang server doesn't see it. - But if I add a member or method in
StaticsConvo.mdit does see it. StaticsConvo.gdhas a few consts but around 20 methods.Constants.gdhas around 70 consts. I will see what happens if I move some of these around.
I'm having the same problem on Godot 4.3
This is also present in v4.4.1.stable.mono.official [49a5bc7b6]
- i am using an external editor (vscode with godot-tools extension)
- i am trying to get a constant from a class defined with
class_name - the bug is intermittent
- the bug prevents the variable from being accessed, leading to
Cannot find member...error and game crash - reloading the project fixes the issue
A couple screenshots for reference:
I am also experiencing what I believe to be the same bug. Unfortunately I'm using 4.2.1, so I assume that means I'm never going to be able to just easily update and get a fix.
Anyways, I define a static method in one file like this:
class_name FullLocationInfo extends RefCounted
...
static func relative_push_math(origin : FullLocationInfo, towards : FullLocationInfo, use_origin_layer := true) -> FullLocationInfo:
and I try to access it in another file (FullLocationInfo.relative_push_math) but I get a parse error and it says the method can't be found.
I've also found that autocomplete does not show the method under suggestions after I press period... however, if I close and reload the program, this all goes away, as people have said, until I make further changes.
I even tried completely renaming the function (I called it "generic"), closing/opening the editor, then naming the function back to what it was named earlier ("relative_push_math"). This has the even less desirable effect of not throwing a parse error when I try to reference FullLocationInfo.generic even though the function has been completely deleted/commented out, and/or renamed; and also, autocomplete suggestions still show "generic" in the list even though, again, it doesn't exist.
These two files together are a combined 100-110 lines, including lots of comments... so I don't think the editor should be having any trouble keeping track of that exactly. The rest of my project is much larger, but I just wanted to make sure and mention it's at least not like the two relevant files that would be too long on their own.
Finally want to mention, I don't think I even have a single preloading function in any of my files... but I will probably double-check tomorrow when I have more time.
edit: I confirmed, I do not have any preloads. The closest thing is a basic "load()" that's only triggered while running the game, in an autoloader's "_ready" function
Also, within the FullLocationInfo file, I have no consts defined. It has 8 functions and 2 class member variables. Across my entire project, I have only 3 consts defined. Two are strings and one is an int (calculated with bitwise math on a builtin enum).
I think I may have found out what the problem is...
I had a bunch of circular dependancies in my code. This does not cause any errors to pop up, nor does it stop the program from working in most situations as far as I can tell-- however, I ran into a different issue (won't get into it) that seemed like it might have been caused by the circular dependancies, so I went through all my code, completely reorganized it, and though I'm not a huge fan of how it is now, things slowly started automatically updating correctly again as I went through making everything linearly dependant (if that's the right way to say it). That is to say, now, when I save my code, it seems that I once again get the correct code completion suggestions and do not get false compilation errors.
Since I'll be trying not to have any more circular dependancies for the rest of this project, I'll make sure to come back and mention if I have the problem again.
Even if I do have the problem again though, I think it's worth checking into if someone has the time... though if this is the problem, it honestly sounds very difficult to fix.