Severe Sound Loading Failure — 50–68% of Players Unable to Hear Music

Experience context:

  • Experience is a stage-based parkour with different songs for each stage.
  • The experience holds about 30 songs in total as assets.
  • Each song’s duration is about 1.5-2.5 minutes long, and then it loops.
  • Experience link: Electric Dash - Roblox

Error report (last 24hrs):


The number of errors I receive due to sound related issues is extremely concerning.
I have calculated that between 50%-68% of my entire player base encounters sound issues.
Some player’s are even surprised to learn that the game incorporates music.
This greatly hinders the experience, as music is a very critical aspect to it.

Code for playing music:

local ContentProvider = game:GetService("ContentProvider")
local Players = game:GetService("Players")
local RS = game:GetService("ReplicatedStorage")
local SoundService = game:GetService("SoundService")

local GameConfig = require(RS.Configs.GameConfig)
local Signals = require(RS.Signals)
local Sounds = require(RS.Sounds)

local player = Players.LocalPlayer

local musicVolume: number = GameConfig.getCurrentSettings().MUSIC_VOLUME
local musicFolder = Instance.new("Folder"); musicFolder.Name = "Music"; musicFolder.Parent = SoundService
local playingFolder = Instance.new("Folder"); playingFolder.Name = "Playing"; playingFolder.Parent = musicFolder

local clientStagesByIndex: {}

local mapNum: number

local currentSong = Instance.new("Sound"); currentSong.Parent = musicFolder

local MAX_RETRIES = 3
local SAFE_LOAD_TIMEOUT_SECONDS = 5



local function safeLoadSound(sound: Sound): boolean
    -- Try to preload; pcall so moderation errors don’t crash
    local success, err = pcall(function() ContentProvider:PreloadAsync({ sound }) end)
    if not success then
        warn("Failed to preload sound:", sound.Name, "Error:", err)
        -- If it's a permission or moderated asset issue, retrying won't help.
        -- You might want to check the error message for keywords.
        return false
    end
    if sound.IsLoaded then return true end

    for attempt = 1, MAX_RETRIES do
        print(`Sound {sound.Name} not loaded, waiting for it to load... (Attempt {attempt})`)
        local loaded = false
        local conn
        conn = sound.Loaded:Connect(function() loaded = true; conn:Disconnect() end)

        local start = tick()
        local timeout = SAFE_LOAD_TIMEOUT_SECONDS * attempt

        while tick() - start < timeout do if loaded then break end; task.wait(0.1) end
        if conn.Connected then conn:Disconnect() end

        if loaded then return true end
    end

    warn(`Song failed to load after {MAX_RETRIES} attempts.`)
    return false
end

local function togglePlaySong(songName: string): boolean
	print(`Toggling song: {songName}`)
    if currentSong and currentSong.Name == songName then
        if currentSong.IsPlaying then currentSong:Pause() else currentSong:Resume() end
    else
        if GameConfig.CURRENT_PLACE_TYPE == "MAIN" and songName ~= clientStagesByIndex["Stage"..mapNum].song then 
            Signals.BE.ToggleStageMusic:Fire(false) 
        end
        if currentSong and currentSong.Name ~= songName then 
            currentSong:Stop(); currentSong.SoundId = ""; currentSong.Name = ""; currentSong.Parent = musicFolder
        end

        local songData = Sounds.Music[songName]
        if not songData then 
            warn("Song", songName, "not found in Sounds.Music! Player:", player.Name); return false 
        end

        local prevSongName, prevSoundId = currentSong.Name, currentSong.SoundId
        currentSong.Name = songName; currentSong.SoundId = "rbxassetid://"..songData.id
        if not safeLoadSound(currentSong) then 
            currentSong.Name = prevSongName; currentSong.SoundId = prevSoundId; return false 
        end
        currentSong.Parent = playingFolder; currentSong.Volume = musicVolume; currentSong.Looped = true
        currentSong.TimePosition = songData.timePosition or 0; currentSong:Play()
    end
	
    return true
end

local function playStageSong()
	for i = mapNum, 1, -1 do
		local stageSongName: string = clientStagesByIndex["Stage"..i].song
		local success: boolean = togglePlaySong(stageSongName)
		if success ~= false then break end
	end
end

local function onMapUpdated(newMapName: string?, newMapNum: number)
	if newMapNum == mapNum then return end 
	mapNum = newMapNum
	playStageSong()
end

local function init()
    clientStagesByIndex = Signals.BF.GetClientMapsByIndex:Invoke()
    
    local mapNum = Signals.BF.GetStageNum:Invoke()
    onMapUpdated(nil, mapNum)
    
    Signals.BE.NextMapTouchProcessed.Event:Connect(function(nextMapNum: number) onMapUpdated(nil, nextMapNum) end)
    Signals.BE.TogglePlaySong.Event:Connect(function(songName: string) togglePlaySong(songName) end)
    Signals.RE.MapUpdated.OnClientEvent:Connect(onMapUpdated)
end

init()

I have implemented a safeLoadSound function which tries 3 times to load the sound, however this hasn’t seemed to help. Some clients are just not able to ever load the sound.

Expected behavior

I would like at least 80% of my player base to be able to properly play the game with the music playing.
I would like to greatly reduce the timeout issues, and perhaps even the OutOfMemory issues.

Hey @solisade,
It looks like the failure rates for assets being loaded into your experience is at 0.4% (failures caused by networking, invalid asset permissions, etc.), which is less than the current failure rate across all experiences (~0.8%).

Specifically the “Failed to decompress asset” should be resolved now. Can you verify if you’re still seeing high failure rates in your experience?