Dropdown labels show garbled text: EnumProperty items callbacks don't keep a reference to the returned strings
Dropdown labels show garbled text: EnumProperty items callbacks don't keep a reference to the returned strings
To be honest Claude Code found this for me, after I discovered the issue.
BlenderGIS 2.2.14 · Blender 5.2 · Windows 11
Summary
Every dynamic EnumProperty items callback in the addon builds its item list in
a local variable and returns it. Blender's Python API requires the caller to keep
a reference to those strings; otherwise they are garbage-collected while Blender
is still using them, and the UI draws freed memory.
The visible result is mojibake in dropdowns. It is most obvious on entries whose label contains non-ASCII characters, which are heap-allocated rather than interned and so are reused sooner.
From the bpy.props.EnumProperty docs:
There is a known bug with using a callback, Python must keep a reference to the strings returned by the callback or Blender will misbehave or even crash.
Steps to reproduce
- Create a mesh object with a non-ASCII name, e.g.
Mühlbacher Höhenweg - DEM. - Georeference the scene (GIS ▸ Geoscene) so the "on mesh" mode is offered.
- GIS ▸ Import ▸ Georeferenced raster, pick any GeoTIFF.
- Set Mode = "Basemap on mesh" and open the Objects dropdown.
Expected: the object's name. Actual: garbled characters. The entry for the ASCII-named objects in the same scene usually renders correctly, which is what makes it look data-dependent.
Cause
operators/io_import_georaster.py:62
def listObjects(self, context):
objs = [] # local - freed on return
for index, object in enumerate(bpy.context.scene.objects):
if object.type == 'MESH':
objs.append((str(index), object.name, "Object named " + object.name))
return objsobjs and the tuples inside it are dropped as soon as the function returns, but
Blender keeps pointing at the strings until the next redraw.
Scope
This is not isolated to one dropdown. Grepping 2.2.14 for def list*(self, context)
finds 19 callbacks and none of them retains a reference:
| file | line | callback |
|---|---|---|
operators/io_import_georaster.py |
62 | listObjects |
operators/io_import_georaster.py |
104 | listSubdivisionModes |
operators/io_import_osm.py |
137 | listObjects |
operators/io_import_shp.py |
107 | listFields |
operators/io_import_shp.py |
124 | listObjects |
operators/io_export_shp.py |
56 | listCollections |
operators/add_camera_exif.py |
226 | listGeoCam |
operators/view3d_mapviewer.py |
363 / 370 / 382 | listSources / listGrids / listLayers |
prefs.py |
95 / 161 / 233 / 246 | listPredefCRS / listOsmTags / listOverpassServer / listDemServer |
operators/nodes_terrain_analysis_reclassify.py |
776 | listSVG |
The ones building labels from user-supplied names are the ones that show it in practice: object names, collection names, shapefile field names, layer names.
Suggested fix
Hold the list at module level so the strings outlive the call:
_objects_enum = [] # module level - keeps the strings alive
def listObjects(self, context):
global _objects_enum
_objects_enum = [
(str(i), o.name, "Object named " + o.name)
for i, o in enumerate(bpy.context.scene.objects) if o.type == 'MESH'
]
return _objects_enumA shared helper would avoid repeating the idiom 19 times, e.g. a small
cached_enum(key, builder) in utils/ that stores each result in a module-level
dict keyed by callback name.
Notes
- The selection still works: the enum's identifier is the index string and
scn.objects[int(self.objectsLst)]resolves correctly. Only the drawn label is corrupt — so the bug is cosmetic, but it makes the dropdown unusable when you cannot read which object you are choosing. - Workaround for users: temporarily rename the object to ASCII before importing.
- Reproduced on Blender 5.2; the caveat is long-standing in the Blender API and is not specific to a Blender version.
Source: domlysz/BlenderGIS