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

Syntax

Immutable-by-default variables

Omitting the var keyword makes a constant. In Verse, data is immutable unless declared with var.

// PROBLEMATIC:
Count:int = 0 # Creates immutable constant
Count := Count + 1 # Error: Cannot modify constant

✅ Solution:

Always use var for mutable variables:

// CORRECT:
var Count:int = 0 # Mutable integer variable
Count := Count + 1 # OK: Count can change
Syntax

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:

MyHi: hi = Material.hi{ New := 0, By := "" }

Here MyHi is declared with type hi, matching the class name.

Bug

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.

async_function():void =
  defer:
    CleanUp() # May not execute if race exits early

✅ Solution:

Avoid relying on defer for critical cleanup in cancellable contexts:

if didExitEarly:
  CleanUp() # Manual cleanup

Until Epic fixes it, manually handle cleanup when coroutines are aborted.

Limitation

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

Persistence

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:

OnBegin():void =
  set GlobalInt[GetSession()] = (GlobalInt[GetSession()] ?: 0)
  # Use GlobalInt[GetSession()] safely from here on
Limitation

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.

# Always check and create player entry
if not MyMap[player]:
  set MyMap[player] = PlayerData{}
API

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:

my_device := class(creative_device):
  OnBegin():void =
    Playspace := GetPlayspace()
    AllPlayers := Playspace.GetPlayers()
Bug

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

Module

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:

TextureModule:
  MyTexture: texture_asset = texture_asset{}

Note: Avoid deep folder nesting - place assets in root of Content or public modules.

Material

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.

UI

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

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.

Performance

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

Spawn

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:

using { /Fortnite.com/Devices }
spawn_a_prop := class(creative_device):
  @editable MyProp:creative_prop_asset = DefaultCreativePropAsset
  OnBegin():void =
    pos := vector3{X:=0,Y:=0,Z:=0}
    transform := transform {Translation:=pos, Rotation:=IdentityRotation(), Scale:=vector3{1,1,1}}
    SpawnProp(MyProp, transform)
API

GetTransform() requires valid object references

GetTransform() only works when called on a valid spawned object reference.

✅ Solution:

spawnedProp := SpawnProp(MyProp, myTransform)
if (Loc := spawnedProp?.GetTransform()):
  Print("Prop at: {Loc.Translation}")
Streaming

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

Deprecation

Deprecated syntax causes warnings

Implicit assignment in if statements is now deprecated: if (set Map[Key] = FallibleCall[])

// DEPRECATED:
if (set Scores[User] = TryGetScore(user)):
  # Do something

✅ Solution:

Split assignments according to Epic's guidance:

// CORRECT:
if (score := TryGetScore(user)):
  set Scores[User] = score
Update

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

Performance

Excessive Update events cause performance issues

Using Update events for frequent operations without throttling can cause frame rate drops.

// PERFORMANCE INTENSIVE:
Update():void =
  CalculateComplexPhysics() # Runs every frame

✅ Solution:

Throttle Update events or use timers:

// OPTIMIZED:
var LastUpdateTime:float = 0.0
Update():void =
  if GetWorldTime() - LastUpdateTime > 0.1:
    CalculateComplexPhysics()
    LastUpdateTime := GetWorldTime()
Memory

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:

# Store as weak reference when possible
var SpawnedObjects:[]weak<creative_device> = []

# Clean up when done
CleanupObjects():void =
  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