Summary When a Multi-Platform Build & Publish for a world is started from the platform that is last in the SDK's platform order (for example Android, with Windows and Android ticked), it never finishes. It builds and uploads Android, builds and uploads Windows, then switches back to Android and builds and uploads it again, and keeps repeating until the user stops it. The cause is VRCMultiPlatformBuild.ShouldContinueMPB . When the starting platform is also the last in the list, it returns true on every return to that platform, because that branch assumes the platform has not been built yet (as in the avatar flow). The world builder has already built it at the start, so the end-of-run check in SetUpNextMPBTarget ( MPBBuiltCount + 1 == MPBPlatformsList.Count ) can never become true. Environment Unity: 2022.3.22f1 VRChat Worlds SDK: com.vrchat.worlds 3.10.3 VRChat Base SDK: com.vrchat.base 3.10.3 Content type: World (Udon / UdonSharp) Platforms ticked: Windows and Android (PC + Quest) Editor open on: Android. The loop only happens when the run is started while on Android. Files involved: VRCMultiPlatformBuild.cs (com.vrchat.base, Editor/VRCSDK/Dependencies/VRChat) and VRCSdkControlPanelWorldBuilder.cs (com.vrchat.worlds, Editor/VRCSDK/SDK3) Steps to reproduce Open a world project with the Editor's active build target set to Android. Open the VRChat SDK Control Panel, Builder tab, and tick both Windows and Android for a multi-platform build. Press Build & Publish. Wait while the SDK builds and uploads Android, switches to Windows, builds and uploads Windows, and switches back to Android. Expected: the run ends when it arrives back on the starting platform, and the "Multi-Platform Upload Finished" notification shows. Actual: the SDK builds and uploads Android a second time, then Windows again, and so on until the user cancels. Root cause The platform list is [Windows, Android] and MPBInitialPlatform is Android, so the starting platform is also MPBPlatformsList.Last() . Tracing the session state through the run: Android (start), count 0: StartMPB sees initial == last, so it does not enter Building. BuildAndUploadMultiPlatform still builds and uploads Android because Android is in the list. Android, count 1: SetUpNextMPBTarget picks Windows, increments the count, state = Switching, switches platform. Windows, count 2: ShouldContinueMPB returns true (initial != current). Windows is built and uploaded. SetUpNextMPBTarget picks Android and increments. Android (back at start), count 2: ShouldContinueMPB reaches if (MPBInitialPlatform == MPBPlatformsList.Last()) return true; and returns true instead of finishing. Android is built and uploaded again. Android, count 3: the SetUpNextMPBTarget end check needs MPBBuiltCount + 1 == MPBPlatformsList.Count , which is 3 == 2 and false. It switches to Windows and increments again. From step 5 the count only grows past the list length, so neither exit is ever reached: not the isMPBFinished = true path in ShouldContinueMPB , and not the early return false in SetUpNextMPBTarget . The initial == last branch in ShouldContinueMPB and the BuiltCount + 1 == Count check in SetUpNextMPBTarget look designed as a pair for a flow where the starting platform is skipped at the beginning and built last (the code comment says this "can happen for Avatar builds"). In VRCSdkControlPanelWorldBuilder.BuildAndUploadMultiPlatform the only skip condition is !_selectedBuildTargets.Contains(EditorUserBuildSettings.activeBuildTarget) , so a starting platform that is in the list is built immediately. That leaves the count one ahead of what the end check expects. This pairing is an inference from reading the code, not something confirmed by running the avatar flow. Suggested fix End the run when it returns to the starting platform and every platform in the list has already been built. In ShouldContinueMPB , the initial == last branch should only return true while the starting platform has not been built yet: ```csharp // If the initial platform is last in the list, only build it again if it has not been built yet if (MPBInitialPlatform == MPBPlatformsList.Last() && MPBBuiltCount < MPBPlatformsList.Count) { return true; } ``` With [Windows, Android] started on Android this gives MPBBuiltCount == 2 on the return trip, so the run falls through to isMPBFinished = true and ReportMPBDone() . If the avatar flow counts differently (it may build the starting platform last and reach this point with a count of Count - 1 ), the same condition keeps that behaviour, but that is unverified. A second option is to make BuildAndUploadMultiPlatform skip the starting platform when it is last in the list and call SetUpNextMPBTarget(cancellationToken, false) , so the count lines up with the existing end check. Local patch used while waiting for an official fix: an early exit at the top of StartMultiPlatformBuild in VRCSdkControlPanelWorldBuilder.cs . When MPB is true, the list has more than one platform, the initial platform is the last in the list and is the active target, and MPBBuiltCount >= MPBPlatformsList.Count , it calls VRCMultiPlatformBuild.ReportMPBDone() , re-enables the UI, shows the "Multi-Platform Upload Finished" notification and returns. This only touches the world builder, so avatar builds are unaffected. Tested: this patch was run in Unity on a real multi-platform publish started from Android, and worked. The run now finishes normally instead of looping. The one-condition change to ShouldContinueMPB above has not been run itself; the tested patch applies the same end-of-run rule from the world builder. Workaround and evidence Workaround without changing the SDK: start the multi-platform publish while the Editor is on Windows, the first platform in the list. The run then goes Windows, Android, back to Windows, and the existing finish check works. After cancelling a looping run, use VRChat SDK > Utilities > Clear Multi Platform Build State, because the state lives in SessionState and survives script reloads. Evidence from the Unity Editor.log of an affected run: A platform switch to Windows was requested ( Requested script compilation because: Switching to platform Windows, Mac, Linux ), followed later by a switch to Android ( Switching to platform Android ). Stack traces in the same log pass through VRCSdkControlPanelWorldBuilder.BuildAndUploadMultiPlatform ( VRCSdkControlPanelWorldBuilder.cs , line 3312), confirming the build and upload ran from the multi-platform path. The log covers a run that the user cancelled, so it shows the switch order but not a final repeat cycle. The repeat was observed directly in the Editor and is described above. Happy to provide the full Editor.log, the Console output, or a screen recording of the loop if useful.