Common Verse Issues & Solutions
A comprehensive guide to troubleshooting common Verse programming problems in UEFN. Learn about known bugs, workarounds, and best practices.
📝 Verse Syntax and Language Quirks
Immutable-by-default variables
Omitting the var keyword makes a constant. In Verse, data is immutable unless declared with var.
Count:int = 0 # Creates immutable constant
Count := Count + 1 # Error: Cannot modify constant
✅ Solution:
Always use var for mutable variables:
var Count:int = 0 # Mutable integer variable
Count := Count + 1 # OK: Count can change
Class/module-level declarations require explicit types
Defining a module or class field without a type causes a compiler error: "Data definitions at class scope must specify their value domain".
✅ Solution:
Give each class-scope definition an explicit type:
Here MyHi is declared with type hi, matching the class name.
Defer cleanup block may not run if async function is cancelled
Code inside defer: is skipped if the function exits early under a race condition. This is a known bug in Verse.
defer:
CleanUp() # May not execute if race exits early
✅ Solution:
Avoid relying on defer for critical cleanup in cancellable contexts:
CleanUp() # Manual cleanup
Until Epic fixes it, manually handle cleanup when coroutines are aborted.
Generic classes are order-sensitive and cannot be referenced across files
You'll see errors like "Can't access a function from a preceding type (3502)" if you try to use a generic class defined in another file.
✅ Solution:
Define and use parametric types in the same .verse file, or merge modules. As a workaround, put the class and its usage together.
Note: This will be improved in "MaxVerse," but for now keep generic classes together.
💾 Scope and Persistence Issues
weak_map(session,...) no longer persists across rounds
In recent UEFN versions, session now refers only to the current round. Module-scoped maps keyed by session reset each round.
✅ Solution:
Initialize state on each round start in a creative_device's OnBegin:
set GlobalInt[GetSession()] = (GlobalInt[GetSession()] ?: 0)
# Use GlobalInt[GetSession()] safely from here on
Global weak_map(player,...) limits
- Only two persistent maps per island
- Max 128 KB per player
- Cannot iterate a weak_map or list its keys
✅ Solution:
Plan around these constraints: consolidate persistent data into classes if needed, and don't try to loop a weak_map.
if not MyMap[player]:
set MyMap[player] = PlayerData{}
GetPlayspace() only works in creative_device classes
Calling GetPlayspace() outside of classes inheriting creative_device yields a type error.
✅ Solution:
Use GetPlayspace() from within a creative_device class:
OnBegin
Playspace := GetPlayspace()
AllPlayers := Playspace.GetPlayers()
Persistence may not work in Playtest or Edit Sessions
Data saved in playtest or edit sessions may not load back properly, especially with "Import Live Data" settings.
✅ Solution:
Avoid depending on persistence during playtesting. Use in-session storage or manually reset state. Test persistence in live sessions or published islands.
📁 Asset and Module Path Issues
Nested asset modules can't be referenced by default
Each folder (module) is <internal> by default, causing "Invalid access of internal module" errors. Marking submodules <public> in Assets.digest.verse may be overwritten by UEFN rebuilds.
✅ Solution:
Keep assets in top-level modules or apply the <public> specifier:
MyTexture: texture_asset = texture_asset{}
Note: Avoid deep folder nesting - place assets in root of Content or public modules.
Materials with non-exposed parameters may not appear in Assets.digest.verse
Only scalar, vector4, and texture parameters are reflected. Boolean or unparameterized textures are ignored, making materials seem "not created" in Verse.
✅ Solution:
Add parameters for any material data you need in Verse. Convert static textures to texture parameters.
Only scalar, vector4, and texture parameters are currently supported.
Verse classes and devices don't appear in editor UI
"Add Verse Class/Device" may be missing from context menus despite Verse being enabled.
✅ Solution:
Use the Verse Explorer panel: Window > Verse Explorer. Right-click in Content Browser to add new Verse scripts/devices. After creating code, build it (Verse menu) for Verse Device assets to appear.
🔄 UEFN Interoperability and Streaming
Verse devices cause map-check errors with World Partition streaming
"Non-streamed actor references streamed actor" errors occur if a Verse device (non-streamed) holds a reference to a streamed actor.
✅ Solution:
Disable world partition streaming: World Settings > World Partition > Setup, uncheck "Enable Streaming". For small maps, this is fine.
Alternative: Bulk-resave devices to clear inconsistent streaming flags.
Pushing Verse changes can fail/hang on large projects
"Push Changes" may freeze ("Session loading...") on large codebases due to packet size limits.
✅ Solution:
Split code into smaller modules. Use "Push Changes" only on smaller edits, or break code into devices that can be reloaded individually.
Many creators restart UEFN to apply changes on big scripts.
⚙️ Common Pitfalls with APIs
Spawning props/actors at runtime can fail silently
Using SpawnProp() in wrong context or with incorrect transform can result in invisible or non-existent props.
✅ Solution:
spawn_a_prop := class(creative_device):
@editable MyProp:creative_prop_asset = DefaultCreativePropAsset
OnBegin
pos := vector3{X:=0,Y:=0,Z:=0}
transform := transform {Translation:=pos, Rotation:=IdentityRotation(), Scale:=vector3{1,1,1}}
SpawnProp(MyProp, transform)
GetTransform() requires valid object references
GetTransform() only works when called on a valid spawned object reference.
✅ Solution:
if (Loc := spawnedProp?.GetTransform()):
Print("Prop at: {Loc.Translation}")
World Partition affects spawned actors/devices
Spawned objects off-screen or under certain streaming conditions may not replicate properly.
✅ Solution:
Disable streaming or ensure spawned actors are in non-streamed areas. Test in both editor and Play mode.
🔄 Version and Deprecation Effects
Deprecated syntax causes warnings
Implicit assignment in if statements is now deprecated: if (set Map[Key] = FallibleCall[])
if (set Scores[User] = TryGetScore(user)):
# Do something
✅ Solution:
Split assignments according to Epic's guidance:
if (score := TryGetScore(user)):
set Scores[User] = score
UEFN updates can break existing Verse code
Some UEFN updates (e.g., 33.10) have broken compiled projects due to changes in the build system.
✅ Solution:
- Keep Verse version in sync with UEFN
- Regenerate Assets.digest.verse (Asset Reflection)
- Recompile all Verse code
- Roll back to previous editor version if needed
- Always back up code before upgrading UEFN
🚀 Performance & Optimization Issues
Excessive Update events cause performance issues
Using Update events for frequent operations without throttling can cause frame rate drops.
Update()
CalculateComplexPhysics() # Runs every frame
✅ Solution:
Throttle Update events or use timers:
var LastUpdateTime:float = 0.0
Update()
if GetWorldTime() - LastUpdateTime > 0.1:
CalculateComplexPhysics()
LastUpdateTime := GetWorldTime()
Memory leaks from unmanaged object references
Keeping references to spawned objects without cleanup can cause memory leaks over time.
✅ Solution:
Use weak references and proper cleanup:
var SpawnedObjects:[]weak<creative_device> = []
# Clean up when done
CleanupObjects()
for object in SpawnedObjects:
if strongObject := object?:
DestroyObject(strongObject)
Found Another Issue?
Help improve this resource by submitting additional Verse issues or solutions.
Submit Issue Report