rojo icon indicating copy to clipboard operation
rojo copied to clipboard

Bug: MeshPart/UnionOperation fails to sync on reconnect - "URL too long" error

Open Froredion opened this issue 3 weeks ago • 1 comments

Summary

When reconnecting Rojo for initial synchronization, MeshPart and UnionOperation instances are created without their mesh data (MeshId/MeshContent is empty). This is caused by two issues:

  1. Roblox behavior change: Instance.new("MeshPart") now succeeds instead of failing, creating an empty MeshPart
  2. URL length limit: The fallback mechanism fails with "URL too long" when serializing many instances

Environment

  • Rojo version: 7.6.x?
  • Roblox Studio: Latest (as of November 2025)
  • OS: Windows 11

Root Cause Analysis

Issue 1: Instance.new("MeshPart") behavior changed

The current code in plugin/src/Reconciler/reify.lua assumes Instance.new("MeshPart") will fail:

local createSuccess, instance = pcall(Instance.new, virtualInstance.ClassName)

if not createSuccess then
    -- Mark this instance and all its children as unapplied
    -- The fallback sync mechanism will handle it
    addAllToPatch(unappliedPatch, virtualInstances, id)
    return
end

Expected behavior: Instance.new("MeshPart") fails → instance goes to fallback → SerializationService creates proper MeshPart with mesh data

Actual behavior: Instance.new("MeshPart") succeeds → creates empty MeshPart → attempts to set MeshContent property → fails because MeshContent is read-only after creation

Debug output showing the issue:

[MESHDBG] reify: Instance.new('MeshPart') for id= 5ef5631551f6a828dc02e4daa5df4001 name= Ball
[MESHDBG] reify: createSuccess= true instance= Ball
...
[MESHDBG] reify: setProperty MeshPart . MeshContent = Content success= false  ← FAILS!

Issue 2: Fallback fails with "URL too long"

Even though the instance ends up in the unapplied patch (due to failed properties), the fallback mechanism in ServeSession:__replaceInstances() fails when there are many instances because all instance IDs are passed in the URL:

[MESHDBG] __replaceInstances: calling serialize API with 1983 ids (includes Ball)
[MESHDBG] __replaceInstances: serialize API ERROR: Unknown HTTP error: URL too long

The serialize API endpoint puts all IDs in the URL query string, which exceeds the HTTP URL length limit (~2000 characters) when there are many instances.

Steps to Reproduce

  1. Create a Rojo project with MeshPart instances that have MeshId/MeshContent set
  2. Start Rojo serve and connect from Studio
  3. Disconnect Rojo
  4. Reconnect Rojo
  5. Observe that MeshPart instances have empty MeshId

Expected Behavior

MeshPart instances should retain their MeshId/MeshContent after reconnecting.

Actual Behavior

MeshPart instances are created with empty MeshId because:

  1. Instance.new("MeshPart") succeeds (creating empty mesh)
  2. MeshContent cannot be set (read-only)
  3. Fallback mechanism fails due to URL length

Proposed Fix

Fix 1: Force MeshPart/UnionOperation to always use fallback

In reify.lua, force these classes to go through the fallback mechanism even if Instance.new succeeds:

local createSuccess, instance = pcall(Instance.new, virtualInstance.ClassName)

-- Force fallback for classes that require SerializationService
local requiresFallback = virtualInstance.ClassName == "MeshPart"
    or virtualInstance.ClassName == "UnionOperation"

if not createSuccess or requiresFallback then
    addAllToPatch(unappliedPatch, virtualInstances, id)
    return
end

Debug Output (Full Trace)

Note: [MESHDBG] prefix is from debug prints we added during investigation to trace the issue.

[MESHDBG] diff: MARK ADDED MeshPart id= 5ef5631551f6a828dc02e4daa5df4001 name= Ball
[MESHDBG] reify: Instance.new('MeshPart') for id= 5ef5631551f6a828dc02e4daa5df4001 name= Ball
[MESHDBG] reify: createSuccess= true instance= Ball
[MESHDBG] reify: setProperty MeshPart . MeshContent = Content success= false
[MESHDBG] reify: UNAPPLIED PROPERTIES for MeshPart id= 5ef5631551f6a828dc02e4daa5df4001
[MESHDBG] applyPatch: Processing ADDED MeshPart id= 5ef5631551f6a828dc02e4daa5df4001 name= Ball
[MESHDBG] applyPatch: SKIP MeshPart id= 5ef5631551f6a828dc02e4daa5df4001 - already exists in instanceMap
[MESHDBG] ServeSession __initialSync: unapplied UPDATE id= 5ef5631551f6a828dc02e4daa5df4001 name= Ball
[MESHDBG] __replaceInstances: calling serialize API with 1983 ids (includes Ball)
[MESHDBG] __replaceInstances: serialize API ERROR: Unknown HTTP error: URL too long

After applying batching fix:

Debug output after implementing the batching solution:

[MESHDBG] __replaceInstances: processing 1983 ids in batches of 50 (includes Ball)
[MESHDBG] __replaceInstances: Ball is in batch 7 with 50 ids
[MESHDBG] __replaceInstances: deserialized Ball, class= MeshPart MeshId= rbxassetid://554089739
[MESHDBG] __replaceInstances: after all batches, modelSuccess= true total replacements= 1983
[MESHDBG] __replaceInstances: replacing Ball
[MESHDBG]   oldInstance= ...Ball oldMeshId=   ← Empty before fix
[MESHDBG]   replacement class= MeshPart newMeshId= rbxassetid://554089739  ← Correct after fix
[MESHDBG] __replaceInstances: DONE - Ball final state: MeshId= rbxassetid://554089739

Related Files

  • plugin/src/Reconciler/reify.lua - Instance creation logic
  • plugin/src/Reconciler/applyPatch.lua - Patch application
  • plugin/src/ServeSession.lua - Fallback mechanism (__replaceInstances)
  • src/web/api.rs - Server-side serialize endpoint

Additional Context

This bug affects any project with MeshPart or UnionOperation instances. The issue became apparent after a Roblox update that changed the behavior of Instance.new("MeshPart") from failing to succeeding.


Could there be a better solution?

Froredion avatar Dec 01 '25 05:12 Froredion

I just confirmed the official Rojo codebase does have the same issues described in the bug report.

Simple repro:

  1. Create a Rojo project with a .rbxm file in the Workspace, do not sync yet. Let's say a Rig
  2. now, do the initial sync. It will load the Rig with many MeshParts but no MeshId.

Froredion avatar Dec 01 '25 06:12 Froredion