Failing Verse Persistence after 38.0 for Ambiguous identifier even after fixing or fully removing file - Unable to publish update!

Summary

Trying to push a map update after 38.0 with NO changes to the verse file in question OR to ANY Persistent data the Publish Validator flags the Spatial Math library as ambiguous preventing publishing - Persistence files are version locked in perforce all classes have not been changed. File in question was no not edited either (between updates).

Removing the file from the project does NOT clear the error!


Public Code: 0742-6605-8749
Private Codes affected -
9147-4585-7319 - File fully deleted from project (11/06/2025, 09:15 PM)
7874-9819-4516 - File vhs_player_device.verse -
11/06/2025, 07:49 AM - Fixed Ambiguity with full Spatial Math Pathing
1389-0804-5922 - First Encounter (11/05/2025, 10:25 PM)

Please select what you are reporting on:

Verse

What Type of Bug are you experiencing?

Publishing

Steps to Reproduce

Trying to push a map update after 38.0 with NO changes to the verse file in question OR to ANY persistable data the Publish Validator flags the Spatial Math library as ambiguous preventing publishing -

Expected Result

Fixing the type error OR removing the file should resolve the publishing error -

Observed Result

Unable to publish Map Update - Select Private Version:

9147-4585-7319

VerseBuild: Error: C:/build/FortniteGame/Plugins/GameFeatures/55bb032d-4f01-350e-2e20-ac9e49b43ac4/sys/published/Content/Verse/Scripts/VhsSystem/vhs_player_device.verse(362,22, 362,31): Script error 3588: Ambiguous identifier; could be (/UnrealEngine.com:)Temporary or (/Verse.org:)Temporary
errors.com.epicgames.cookplugin.versebuildscripterror

Platform(s)

windows

Island Code

0742-6605-8749

Upload an image

Additional Notes

We’ve fixed the type error with no resolution.
We removed the file from the project no resolution.

Publishing is caching a non-existent file!

This is not a persistence error, yet the compiler and publishing check is flagging it as such.

So I can confirm that this is on the Epic’s servers, I went into my local cache files to ensure a fresh cook, %APPDATA%..\Local\UnrealEditorFortnite and removed all folders containing the project name “RewinDead” including the ValkyrieUploadTemp\ and Saved\VerseProject and VerseSnapshots. This was to ensure a completely fresh copy locally that did not even contain the file “vhs_player_device.verse”.

The EPIC servers ARE caching this file somewhere! As I removed it from my workspace, and from the entire project and local cache and cook locations it would not have been pushed up, thus the build server should not have had it.

I DO NOT USE URC - We use Perforce so we can control our builds and enviroment to prevent issues like this. I had one of my team members perform the same tests and the results were the same showing that the epic cook still was finding this now removed “vhs_player_device.verse”. I would assume we wouldn’t be hitting the same pre-cooked containers.

This tells us the NOMAD pipeline is pre-caching and not flushing for new builds. I’ve worked in server architecture and security for 20 years and understand how the pipeline is setup thanks to a talk from HashiCorp (Cooking with Nomad: Powering the Fortnite creator ecosystem)

I’ve even restored the file from perforce to the EXACT file with no changes (exact MD5 checksum) of the version that worked - PRIVATE CODE: 9258-3078-4302 11/04/2025, 12:18 PM

As you can see there is no classes and the class referenced is also not a persistable class:

using { /Verse.org/Simulation }
using { /Fortnite.com/Devices }
using { /Fortnite.com/Characters }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /UnrealEngine.com/Temporary/SpatialMath }
using { /Verse.org/Assets }
using { /UnrealEngine.com}
using { /Verse.org}
using { VhsSystem }
using { SearchSystem }

###############################################################################
### VHS Player Device (TV/Monitor in world)
### When player interacts, spawns collectible tape and turns off display
###############################################################################

# Material wrapper that holds an instantiated material for parameter access
<#
vhs_material_instance<public> := class:
    # The instantiated material we can modify
    Material<public> : Flipbooks.tape_0000.MI_VHS_ParentMaterial = Flipbooks.tape_0000.MI_VHS_ParentMaterial{}

    # Always set the material on the prop first
    SetMaterial(ScreenProp:creative_prop, ?Index:int = 0) : void =
        ScreenProp.SetMaterial(Material, ?Index:=Index) 
    
    # Set preview parameters
    SetPreviewParams<public>(?ScreenProp:?creative_prop = false) : void =
        if (ValidProp := ScreenProp?) then ValidProp.SetMaterial(Material)
        set Material.NoiseIntensity = 0.3
        set Material.ScreenOff = 0.0
        set Material.FlipbookUVs = 0.0
        set Material.FPS = 4.0
    
    # Set playback parameters
    SetPlaybackParams<public>(FPS : float, Duration : float, TotalFrames : float, ?ScreenProp:?creative_prop = false) : void =
        if (ValidProp := ScreenProp?) then ValidProp.SetMaterial(Material)
        set Material.NoiseIntensity = 0.0
        set Material.ScreenOff = 0.0
        set Material.FlipbookUVs = 0.0
        set Material.FPS = FPS
        set Material.PlayRate = 1.0
        set Material.Duration_Seconds = Duration
        set Material.Total_Frames = TotalFrames
    
    # Update flipbook frame
    UpdateFrame<public>(FrameValue : float, ?ScreenProp:?creative_prop = false) : void =
        if (ValidProp := ScreenProp?) then ValidProp.SetMaterial(Material)
        set Material.FlipbookUVs = FrameValue
    
    # Turn screen off
    SetScreenOff<public>(?ScreenProp:?creative_prop = false) : void =
        if (ValidProp := ScreenProp?) then ValidProp.SetMaterial(Material)
        set Material.ScreenOff = 1.0
        set Material.NoiseIntensity = 0.0
    
    # Get total frames
    GetTotalFrames<public>() : float = Material.Total_Frames
#>

# Constructor to create material instance - directly instantiate from source material
<#
MakeVHSMaterial<constructor>(TapeMaterial : ?material)<transacts><decides>:= vhs_material_instance:
    # Typecast Tapematerial to ensure it's a valid child material
    # Function will fail if not
    Material :=  Flipbooks.tape_0000.MI_VHS_ParentMaterial[TapeMaterial?]


MakeVHSMaterial<constructor>(Src : vhs_material_instance)<transacts>:= vhs_material_instance:
    # Make a new class with static parameter
    Material :=  Src.Material
#>


vhs_player_device<public> := class<unique>(creative_device):
    Logger : log = log{Channel := log_vhs}
    
    # TV/Monitor prop with VHS material
    @editable
    var ScreenProp<public> : creative_prop = creative_prop{}

    @editable
    var ScreenMaterial : Flipbooks.MM_VHS_PARENT_UI = Flipbooks.MM_VHS_PARENT_UI{}

    # This tape's specific material instance for playback
    @editable
    var TapeMaterial<public> : Flipbooks.MM_VHS_PARENT_Prop = Flipbooks.MM_VHS_PARENT_Prop{}

    @editable
    var TapeTexture<public> : ?texture = false
    
    # Instantiated material wrapper for parameter access
    # var VHSMaterial<private> : vhs_material_instance = vhs_material_instance{}
    #@editable
    #VHSMat : vhs_material = vhs_material{}

    # This tape's resource ID (e.g., 5101, 5204, 5306)
    @editable
    TapeResourceID<public> : int = 5101
    
    # Playback properties - configure per device in UEFN
    @editable
    FlipbookFrames<public> : int = 80
    @editable
    PlaybackFPS<public> : float = 4.0
    @editable
    DurationSeconds<public> : float = 20.0
    @editable
    StoryID<public> : vhs_story_id = vhs_story_id.None
    @editable
    TapeNumber<public> : int = 0  # Position within story (1, 2, 3...)
    
    # Audio device playing static/preview
    @editable
    AudioDevice<public> : ?audio_player_device = false

    @editable
    UIPreviewAudio: ?audio_player_device = false

    @editable
    StaticAudio: ?audio_player_device = false
    
    # Collectible to spawn when player interacts
    @editable
    TapeCollectible<public> : collectible_object_device = collectible_object_device{}
    
    # Interaction method
    @editable
    InteractionTrigger<public> : ?trigger_device = false

    @editable
    PlayTapeUI: conversation_device = conversation_device{}

    # Use SearchSystem integration (tag with vhs_player_tag)
    #@editable
    #UseSearchSystem<public> : logic = true

    var TapeData : vhs_tape_data = vhs_tape_data{}
    
    # State tracking - lives on the device
    var HasBeenCollected<private> : logic = false
    var HasBeenWatched<private> : logic = false
    var IsScreenOn<public> : logic = true
    
    # Public state accessors
    IsCollected<public>()<transacts> : logic = HasBeenCollected
    IsWatched<public>()<transacts> : logic = HasBeenWatched
    MarkAsCollected<public>()<transacts> : void = set HasBeenCollected = true
    MarkAsWatched<public>()<transacts> : void = set HasBeenWatched = true
    
    OnBegin<override>()<suspends> : void =
        Logger.Print("VHS Player Device initializing (Tape ID: {TapeResourceID})...")
        
        # Register this tape's data in the map - this preserves the material instance
        # Use ScreenMaterial which is the typed material configured in UEFN
        ResourceName := if (ResourceDef := ResourceSystem.GetResource[TapeResourceID]):
            ResourceDef.Name
        else:
            "Unknown"
        
        # Create source data object with the ScreenMaterial from UEFN
        # This preserves the unique flipbook_texture baked into each material instance
        SourceData := vhs_tape_data:
            ResourceID := TapeResourceID
            TapeName := ResourceName
            StoryID := StoryID
            TapeNumber := TapeNumber
            FlipbookFrames := FlipbookFrames
            PlaybackFPS := PlaybackFPS
            DurationSeconds := DurationSeconds
            ScreenProp := ScreenProp
            TapeMaterial := option{ScreenMaterial}  # Store the typed material instance
            AudioDevice := AudioDevice
        
        # Use constructor to create final tape data
        set TapeData = MakeTapeData(SourceData)
        
        RegisterTapeData(TapeData)
        Logger.Print("VHS Player {TapeResourceID} registered tape data with material instance")

        # Fall back to trigger/button
        if (Trigger := InteractionTrigger?):
            Logger.Print("VHS Player {TapeResourceID} configured with Trigger for interaction")
            Trigger.TriggeredEvent.Subscribe(OnVHSTriggered)
        
        # Subscribe to conversation events
        PlayTapeUI.OnConversationEvent.Subscribe(HandleConversationEvent)
        Logger.Print("VHS Player Device {TapeResourceID} ready")

    SetPreview(Agent:agent):void = 
        Logger.Print("Setting VHS Player {TapeResourceID} to preview mode")
         # Configure THIS tape's unique material for preview with noise
        ScreenProp.SetMaterial(TapeMaterial, ?Index:=0 )
        if (FlipbookTexture := TapeTexture?):
            set TapeMaterial.FlipbookTexture = FlipbookTexture
        #var PreviewMaterial : Flipbooks.MM_VHS_PARENT_UI = TapeMaterial
            set TapeMaterial.NoiseIntensity = 0.3  # Light noise for preview
            set TapeMaterial.ScreenOff = 0.0
            set TapeMaterial.FlipbookUVs = 0.0
            set TapeMaterial.FPS = 4.0
            set TapeMaterial.PlayRate = 0.5  # Slow playback for preview
            Logger.Print("Texture set for {TapeResourceID} Succesfully")
        else:
            ScreenProp.SetMaterial(ScreenMaterial) # REMOVE AFTER TESTING 11/1/25 21.15
            Logger.Print("FAILED to set texture for Screenprop {TapeResourceID}", ?Level:=log_level.Error)
        
        # Apply the configured material (keeps the unique flipbook texture)
        #ScreenProp.SetMaterial(PreviewMaterial, ?Index:= 0)
        # Retrieve material from tape data map - this preserves the actual instance type
        #if (TapeData := GetTapeData[TapeResourceID]):
        #    if (Mat := TapeData.TapeMaterial?):
                # Typecast to parent to access parameters
        #        if (VHSMat := Flipbooks.MM_VHS_PARENT_UI[Mat]):
        ##            set VHSMat.NoiseIntensity = 0.3
        #            set VHSMat.ScreenOff = 0.0
        #            set VHSMat.FlipbookUVs = 0.0
        #            set VHSMat.FPS = 4.0
        #        ScreenProp.SetMaterial(Mat)  # Apply the original material, not the typecast
        #            Logger.Print("Applied preview material for tape {TapeResourceID}")

        # Play static audio
        if(AudioPlayer := StaticAudio?):
            AudioPlayer.Play()

        # Enable the preview audio
        if (PreviewAudio := UIPreviewAudio?):
            PreviewAudio.Enable()
            PreviewAudio.Register(Agent)
        # Enable main audio device
        if (AudioPlayer := AudioDevice?):
            AudioPlayer.Enable()
            AudioPlayer.Register(Agent)

    
    ###########################################################################
    # Public methods for broadcast playback
    ###########################################################################
    
    # Play this tape on a target screen (or this device's screen if none provided)
    # Returns when playback is complete
    Play<public>(Player : agent, ?TargetScreen : ?creative_prop = false)<suspends> : void =
        # Determine which screen to play on
        PlaybackScreen := if (Target := TargetScreen?):
            Target
        else:
            ScreenProp
        
        # Retrieve material from tape data map - this preserves the actual instance type
        if (TapeDataSession := GetTapeData[TapeResourceID]):
            if (Mat := TapeDataSession.TapeMaterial?):
                # Typecast to parent to access parameters
                if (VHSMat := Flipbooks.MM_VHS_PARENT_UI[Mat]):
                    # Set playback parameters
                    set VHSMat.NoiseIntensity = 0.0  # Clean playback
                    set VHSMat.ScreenOff = 0.0
                    set VHSMat.FlipbookUVs = 0.0
                    set VHSMat.FPS = PlaybackFPS
                    set VHSMat.PlayRate = 1.0
                    set VHSMat.Duration_Seconds = DurationSeconds
                    set VHSMat.Total_Frames = FlipbookFrames * 1.0
                    
                    PlaybackScreen.SetMaterial(Mat)  # Apply the original material, not the typecast
                    Logger.Print("Applied material for tape {TapeResourceID} to screen")
                    
                    # Start audio playback
                    if (AudioDev := AudioDevice?):
                        AudioDev.Play(Player)
                        Logger.Print("Audio playback started for tape {TapeResourceID}")
                    else:
                        Logger.Print("Warning: No audio device for tape {TapeResourceID}", ?Level := log_level.Warning)
                    
                    # Animate playback over duration by updating FlipbookUVs
                    UpdateInterval := 0.1  # Update every 100ms
                    var ElapsedTime : float = 0.0
                    
                    loop:
                        if (ElapsedTime >= DurationSeconds):
                            break
                        
                        # Update material with new frame
                        Progress := ElapsedTime / DurationSeconds
                        CurrentFrame := Progress * VHSMat.Total_Frames
                        set VHSMat.FlipbookUVs = CurrentFrame
                        PlaybackScreen.SetMaterial(Mat)  # Apply the original material

                        Sleep(UpdateInterval)
                        set ElapsedTime += UpdateInterval
                    
                    # Stop audio when complete
                    if (AudioDev := AudioDevice?):
                        AudioDev.Stop(Player)
                        Logger.Print("Audio playback stopped for tape {TapeResourceID}")
                    
                    # Mark as watched
                    MarkAsWatched()
                    
                    Logger.Print("âś… Tape {TapeResourceID} playback complete")
        #else:
        #    Logger.Print("ERROR: Cannot play tape {TapeResourceID} - no material instance", ?Level:=log_level.Error)    # Handle player interaction
    OnVHSTriggered<public>(MaybeAgent : ?agent) : void =
        if (Agent := MaybeAgent?):
            Logger.Print("VHS Player {TapeResourceID} triggered by player")
           spawn {OnPlayerInteract(Agent)}
        else:
            Logger.Print("VHS Player {TapeResourceID} triggered with no agent", ?Level := log_level.Warning)
    
    OnPlayerInteract<private>(Agent : agent)<suspends> : void =
        if (HasBeenCollected?):
            Logger.Print("Tape {TapeResourceID} already collected from this player")
            return
        
        Logger.Print("Player interacting with VHS player - playing tape {TapeResourceID}")

        # Start Preview mode
        SetPreview(Agent)

         # Show the conversation UI for playing the tape
        PlayTapeUI.InitiateConversation(Agent)


 
    HandleConversationEvent(EventData : tuple(agent, int)) : void =
        Agent := EventData(0)
        EventInt := EventData(1)
        
         # EventInt 3 = Take Tape
         case (EventInt):
            1 => block: # STOP TAPE
                    Logger.Print("Player chose to Stop tape {TapeResourceID} again")
                    if(PreviewAudio := UIPreviewAudio?):
                        PreviewAudio.Stop(Agent)
                        Logger.Print("Stopping Preview Audio for UI Tape {TapeResourceID}")
                    PlayTapeUI.HideConversation(Agent)
            2 => block: # PLAY TAPE
                    Logger.Print("Player chose to Start playing tape preview {TapeResourceID}")
                    if(PreviewAudio := UIPreviewAudio?):
                        PreviewAudio.Play(Agent)
                        Logger.Print("Playing Preview Audio for UI Tape {TapeResourceID}")
            3 => block: # EJECT TAPE
                    Logger.Print("Player Ejecting Tape {TapeResourceID}")
                    if(PreviewAudio := UIPreviewAudio?):
                        PreviewAudio.Stop(Agent)
                        Logger.Print("Stopping Preview Audio for UI Tape {TapeResourceID}")
                    OnTakeTape(Agent)
                    PlayTapeUI.EndConversation(Agent)
            _ => Logger.Print("Unknown conversation event {EventInt} for tape {TapeResourceID}", ?Level := log_level.Warning)

    
    OnTakeTape(Agent:agent):void =
        Logger.Print("Player chose to take the tape - {TapeResourceID}")
        # Turn off screen (set to black)
        TurnOffScreen()
        
        # Stop audio
        StopAudio()
        
        #EjectTapeAudio.Play() - This should play inside the conversation_device UI widget
        # Get the location of the screenProp to spawn the tape collectible
        # ScreenLocation := Temporary.SpatialMath.FromTransform(ScreenProp.GetTransform())

        OffsetScreenLocation := (SpatialMath.vector3{Left := 0.0, Up := 50.0, Forward := 200.0}) # Offset above the screen
        XYZOffset := Temporary.SpatialMath.FromVector3(OffsetScreenLocation)
        XYZPosition := Temporary.SpatialMath.FromTransform(ScreenProp.GetTransform())
        # Ensure both are vector3, then add
        OffsetTranslation := Temporary.SpatialMath.FromVector3(SpatialMath.vector3{
            Left := XYZPosition.Translation.Left + OffsetScreenLocation.Left,
            Up := XYZPosition.Translation.Up + OffsetScreenLocation.Up,
            Forward := XYZPosition.Translation.Forward + OffsetScreenLocation.Forward
        })
        NewTransform := Temporary.SpatialMath.FromTransform(transform{
                Translation := OffsetTranslation
                #Rotation := Rotation{}#Temporary.SpatialMath.FromVector3(XYZPosition.rotation{}) # Keep the vfx starting rotation
            })
        # Spawn collectible
        if(ResourceName := ResourceSystem.GetResource[TapeResourceID].Name):
            Logger.Print("Spawning tape collectible for resource {ResourceName}")
            ResourceSystem.GetItem(Agent, ResourceName, ?Location := option{NewTransform})

        
        # Register this tape in the VHS session so it appears in the Mix Deck UI
        if (Session := GetVHSSession[Agent]):
            Session.AddCollectedTape(TapeResourceID)
            Logger.Print("Registered tape {TapeResourceID} in VHS session")
        
        # Mark as collected
        set HasBeenCollected = true
    
    # Turn off screen display
    TurnOffScreen<private>() : void =
        # Retrieve material from tape data map and turn off screen
       # if (TapeData := GetTapeData[TapeResourceID]):
            if (Mat := TapeData.TapeMaterial?):
                if (VHSMat := Flipbooks.MM_VHS_PARENT_UI[Mat]):
                    set VHSMat.ScreenOff = 1.0
                    ScreenProp.SetMaterial(Mat)  # Apply the original material
                else:
                    Logger.Print("Unable to Typecast parent material for {TapeResourceID} on TurnOffScreen", ?Level:=log_level.Warning)
        
        set IsScreenOn = false
        
        # Update registry
        UpdateRegistryState()
        
        Logger.Print("Screen turned off for tape {TapeResourceID}")
    
    # Stop audio playback
    StopAudio<private>() : void =
        if (AudioPlayer := AudioDevice?):
            AudioPlayer.Stop()
            Logger.Print("Audio stopped for tape {TapeResourceID}")

    # State is now stored directly on device - no registry needed
    UpdateRegistryState<private>()<transacts> : void =
        # Collection state stored in HasBeenCollected
        Logger.Print("Updated VHS player state for tape {TapeResourceID}")
    
    # Public method to check screen state
    IsOn<public>() : logic =
        IsScreenOn
    
    # Reset the device (for testing or respawning)
    Reset<public>() : void =
        set HasBeenCollected = false
        set IsScreenOn = true
        
        # Turn screen back on with preview settings using material from map
        #if (TapeData := GetTapeData[TapeResourceID]):
            if (Mat := TapeData.TapeMaterial?):
                if (VHSMat := Flipbooks.MM_VHS_PARENT_UI[Mat]):
                    set VHSMat.NoiseIntensity = 0.3
                    set VHSMat.ScreenOff = 0.0
                    set VHSMat.FlipbookUVs = 0.0
                    set VHSMat.FPS = 4.0
                    ScreenProp.SetMaterial(Mat)  # Apply the original material

        # Restart audio
        if (AudioPlayer := AudioDevice?):
            AudioPlayer.Play()
            Logger.Print("Audio started for tape {TapeResourceID}")
        
        # Hide collectible
        TapeCollectible.Hide()
        
        # Update registry
        UpdateRegistryState()

        Logger.Print("VHS Player {TapeResourceID} device reset")


        

This is the exact MD5 checksum verified file that is currently uploaded and working however the Validation is still failing with the same error -

VerseBuild: Error: C:/build/FortniteGame/Plugins/GameFeatures/55bb032d-4f01-350e-2e20-ac9e49b43ac4/sys/published/Content/Verse/Scripts/VhsSystem/vhs_player_device.verse(362,22, 362,31): Script error 3588: Ambiguous identifier; could be (/UnrealEngine.com:)Temporary or (/Verse.org:)Temporary
errors.com.epicgames.cookplugin.versebuildscripterror

No matter what I try it seams to still use the same path over multiple days of attempting to Publish, it’s always the same - 55bb032d-4f01-350e-2e20-ac9e49b43ac4 - with the same file and error.

Please let me know what other information I can provide to assist.

So it is definitely caching on Epic’s servers - I edited the file in question (fixing any errors) and moved the Line of where the “problem” is from line 362 to line 369.

After cleaning the cache files again, stopping and creating a new session and memory test the error when publishing is still showing line 362! It’s not getting/taking/reading the new file.

I think this is the same problem @giovafncreative is having except they are using URC and we are using Perforce.

For reference their Bug Report is - [CRITICAL] Cannot update my map with private versions made after 38.00 - General / Issues and Bug Reporting - Epic Developer Community Forums

1 Like

Logs for epic -

Logs.zip (28.9 MB)