Model keeps parenting to nil

You can write your topic however you want, but you need to answer these questions:

  1. What do you want to achieve? Keep it simple and clear!
    I have a car, specifically a local one, which self-assembles using suspension parts.
    I recently converted my server version to a local version, which was fine, but I need to workaround some models parenting to nil.

  2. What is the issue? Include screenshots / videos if possible!
    The title says it all, the “sus” model is parenting to nil (the model that contains wheels and axels), which makes all the other scripts reset, if they’re inside the suspension (which I have a few of).
    To debug the script, I added a print statement that prints out “LOCAL NIL” whenever the script needs to reparent the model. The problem also occurs when the player goes a certain amount of studs away from the car, but not all the time.
    Video:

  3. What solutions have you tried so far? Did you look for solutions on the Developer Hub? I don’t see any relevant solutions on the Developer Hub. I tried several other solutions, such as reparenting the model, but the scripts inside still tend to reset.

After that, you should include more details if you have any. Try to make your topic as descriptive as possible, so that it’s easier for people to help you!

If this helps, here’s the “main” script. Parts of it were coded years ago, and since this is not designed to be replicated, it is a LocalScript. RaycastModule is a module I made that will convert the arguments from the old workspace:FindPartOnRay to workspace:Raycast. I’m also working on removing the 2nd argument from Instance.new, but that’s for another day.

wait(0.2)
local set = script.Config

local SuspensionEnabled = set.SuspensionEnabled.Value
local susLength = set.Max
local susTargetLength = set.Length
local minSusLength = set.Min

local stiffness = set.Stiffness
local pow = set.Pow
local veldamp = set.Damp
local wheelsBreakable = set.WheelBreakable
local wheelHealth = set.WheelHealth

local raycast = require(script.RaycastModule)
local mass = 0

local axelReflectance = 0.2
local axelColor = BrickColor.new("Dark stone grey")
local axelMaterial = Enum.Material.Plastic
--script.Parent.sus:MakeJoints()
local base = script.Parent.Base

local w = Instance.new("Model", script.Parent:WaitForChild("sus"))
w.Name = "Wheels"
local w2 = Instance.new("Model",w)
w2.Name = 'pWheels'
local ax = Instance.new("Model", script.Parent:WaitForChild("sus"))
ax.Name = "axels"

function findNearestThruster(obj)
	local range = 50
	local closest
	for _, other in pairs(script.Parent.sus:GetDescendants())do
		if other:IsA("BasePart")and string.match(string.lower(other.Name), "thrust")and other.Parent==script.Parent.sus and other~=obj then
			local dist = (other.Position-obj.Position).Magnitude
			local dot = (other.Position-obj.Position).Unit:Dot(obj.CFrame.LookVector)
			if dist<range and math.abs(dot)<0.1 then
				range = dist
				closest = other
			end
		end
	end
	return closest
end
function clearSrufaces(part)
	part.CanCollide = false
	for _, normalId in pairs(Enum.NormalId:GetEnumItems())do
		part[normalId.Name.."Surface"]=Enum.SurfaceType.Smooth
	end
end
function setPrefrences(axel)
	axel.Reflectance = axelReflectance
	axel.Material = axelMaterial
	axel.BrickColor = axelColor
end
function makeAxel(obj, wheel)
	local other = findNearestThruster(obj)
	if other then
		local axel = Instance.new("Part", ax)
		axel.Name = "Axel"
		axel.Size = Vector3.new(1,1,1)
		axel.CFrame = obj.CFrame
		setPrefrences(axel)
		axel.Massless = true
		clearSrufaces(axel)
		local axelBase = Instance.new("Part", ax)
		axelBase.Name = "AxelBase"
		local mesh = Instance.new("SpecialMesh",axelBase)
		mesh.MeshType = Enum.MeshType.Sphere
		mesh.Scale = Vector3.new(2,1,1)
		axelBase.Size = Vector3.new(1,1,1)
		axelBase.CFrame = obj.CFrame:Lerp(other.CFrame, 0.5)
		setPrefrences(axelBase)
		axelBase.Massless = true
		clearSrufaces(axelBase)
		weldToBase(axelBase)
		local axelScript = script.axelScript:Clone()
		axelScript.Parent = axel
		axelScript.base.Value = axelBase
		axelScript.target.Value = wheel
		axelScript.Disabled = false

		return axel
	end
end
function isThrust(t)
	local name = string.lower(t.Name)
	local isNamed = string.match(name,'thrust',0)
	local steerThrust = string.match(name,'steer',0)
	local powerThrust = string.match(name,'power',0)
	local casterThrust = string.match(name,'caster',0)
	return {isNamed,steerThrust,powerThrust,casterThrust}
end

local hover = set.HoverEnabled

function weldToBase(part)
	local anchored = part.Anchored
	part.Anchored = true
	local weld = Instance.new("Weld",base)
	local c0 = base.CFrame:ToObjectSpace(base.CFrame)
	local c1 = part.CFrame:ToObjectSpace(base.CFrame)
	weld.C0 = c0
	weld.C1 = c1
	weld.Part0 = base
	weld.Part1 = part
	weld.Name = "Weld from: "..weld.Part0.Name.." to: "..weld.Part1.Name
	part.Anchored = anchored
end

function resizeWheel(wheel, sizeMultiplier)
	local function resize(obj)
		obj.Size*=sizeMultiplier
	end
	for _, obj in pairs(wheel:GetDescendants())do
		if obj:IsA("BasePart")or (obj:IsA("SpecialMesh")and obj.MeshType==Enum.MeshType.FileMesh)or obj:IsA("FileMesh") then
			if obj:IsA("BasePart")then
				resize(obj)
			else
				obj.Scale*=sizeMultiplier
			end
		end
	end
	resize(wheel)
end
local count = 0

for _, obj in pairs(script.Parent.sus:GetChildren())do
	if isThrust(obj)[1] and obj:IsA("BasePart") then

		count+=1

		local customWheelSize = obj:FindFirstChild("CustomWheelSize")
		obj.Transparency = 1
		weldToBase(obj)

		local thrust = Instance.new("BodyThrust")
		local spring = script:WaitForChild("spring"):Clone()
		spring.Parent=obj
		thrust.Parent = obj

		local customWheel = obj:FindFirstChild("CustomWheel")
		if customWheel then
			customWheel = customWheel.Value
		end
		local wheel
		pcall(function()
			wheel = (customWheel or script.Parent.sus:WaitForChild("WheelClone", 1)):Clone()
		end)
		if not wheel then
			wheel = Instance.new("Part")
			wheel.Transparency = 1
			wheel.Size = Vector3.new(2, 2, 2)
		end

		wheel.CanCollide = not SuspensionEnabled
		wheel.Parent = w

		if isThrust(obj)[3]then
			wheel.Parent = w2
		end
		if customWheelSize then
			resizeWheel(wheel, customWheelSize.Value)
		end
		local wx = Instance.new("Weld")
		wx.Name = "wheelWeld"
		wx.Part0=obj
		wx.Part1=wheel
		wx.Parent = wheel

		local wh = wheelHealth.Value

		local axel = makeAxel(obj, wheel)

		local scr = script.wheelScript:Clone()
		scr.Parent = wheel
		if not scr:IsA("ModuleScript")then
			scr.Disabled = false
		else
			scr.Runner.Disabled = false
		end
		scr.caster.Value = isThrust(obj)[4]
		scr.steering.Value = isThrust(obj)[2] and not scr.caster.Value
		scr.thruster.Value = obj
		local sus = script.Parent.sus

		spawn(function()
			local lastMode
			local lastlastMode
			while obj do
				if not sus.Parent then print('LOCAL NIL!') end
				obj.Parent = sus
				sus.Parent = script.Parent
				scr.numWheels.Value = count
				local startPos = obj.Position-(obj.CFrame.UpVector*minSusLength.Value)
				local targetPos = -obj.CFrame.UpVector*(susLength.Value-minSusLength.Value)
				local r = Ray.new(startPos, targetPos)
				local part, pos, mat, normal = raycast(r, script.Parent)
				local dist3 = susLength.Value

				if part and part.CanCollide or hover.Value then
					lastlastMode=true
					local mag = (obj.AssemblyLinearVelocity*obj.CFrame.UpVector).Magnitude
					if lastlastMode and not lastMode and mag>50 and not hover.Value then
						lastlastMode=false
						spring:Play()
						spring.PlaybackSpeed = (mag/512)+0.5
						spring.Volume = mag/64
						if wheelsBreakable.Value and wh>0 then
							local damage = mag/24
							wh-=damage
							if wh<=0 then
								obj:Destroy()
								obj = nil
								wheel.CanCollide = true
								wx:Destroy()
								scr:Destroy()
								axel:Destroy()
								break
							end
						end
					end

					local dist = (obj.Position-(pos or startPos)).Magnitude
					if dist<minSusLength.Value then
						dist = minSusLength.Value
					end
					dist3 = dist
					local st = susTargetLength.Value
					local dx = obj.CFrame:VectorToObjectSpace(obj:GetVelocityAtPosition(obj.Position)):Dot(Vector3.new(0, 1, 0))*(veldamp.Value/(count/4))
					if hover.Value and not part then
						dist = ((obj.Position.Y-base.Position.Y)+susLength.Value)-(base.AssemblyLinearVelocity*Vector3.new(1, 0, 1)/100).Magnitude
						dist3 = susTargetLength.Value
					else
						st+=1
					end
					local stiffForce = math.max(((st - dist)^pow.Value) * ((stiffness.Value*mass*196.2) / st^pow.Value), -math.abs(dx*mass*stiffness.Value*196.2*(base.CFrame.UpVector:Dot(Vector3.new(0, 1, 0)))))
					stiffForce/=count/4
					local n = obj.CFrame:VectorToObjectSpace(normal or obj.CFrame.UpVector)
					local force = n*stiffForce-dx*n*mass
					thrust.Force = force
				else
					thrust.Force = Vector3.new()
				end


				scr.currentDistance.Value = dist3-(wheel.Size.Y/2)

				if not SuspensionEnabled then
					scr.currentDistance.Value = susTargetLength.Value
					thrust.Force = Vector3.new()
				end
				scr.onGround.Value = dist3~=susLength.Value
				if mat and part then
					scr.normal.Value = normal
					scr.groundMaterial.Value = mat.Name
					scr.hitPart.Value = part
					scr.MaxDistance.Value = susLength.Value
					scr.MinDistance.Value = minSusLength.Value
					scr.targetDistance.Value = susTargetLength.Value
				else
					scr.groundMaterial.Value = "Air"
				end
				scr.mass.Value = mass
				lastMode=part~=nil
				game:GetService("RunService").RenderStepped:Wait()
			end
			count-=1
		end)
	end
end
local Pos,sz = script.Parent:GetBoundingBox()
local Pos0 = Vector3.new()
for _, part in pairs(script.Parent.sus:GetChildren())do
	if part:IsA("BasePart")then
		if not isThrust(part)[1] then
			part:Destroy()
		else
			Pos0+=part.Position
		end
	end
end
Pos0/=count
local tb = {}
for _, obj in pairs(base:GetJoints())do
	if obj:IsA("JointInstance")then
		obj.Parent = nil
		table.insert(tb,1,obj)
	end
end
base.CFrame = Pos
base.Position = Pos0
for _, obj in pairs(tb)do
	if obj.Part1 and obj.Part0 then
		if obj.Part1~=base then
			weldToBase(obj.Part1)
		else
			weldToBase(obj.Part0)
		end
	end
end
local mainRod = Instance.new("Part", ax)
mainRod.Massless = true
mainRod.Name = "Main"
local rodWeld = Instance.new("Weld", mainRod)
rodWeld.Part0 = base
rodWeld.Part1 = mainRod
rodWeld.C0 = base.CFrame:ToObjectSpace(Pos)-Vector3.new(0,base.CFrame:ToObjectSpace(Pos).Y,0)
rodWeld.C1 = CFrame.Angles(0,math.pi/2,0)
mainRod.Size = Vector3.new(sz.Z-1.5, 0.75, 1)
setPrefrences(mainRod)
clearSrufaces(mainRod)
mainRod.Shape = Enum.PartType.Cylinder
for _, obj in pairs(script.Parent:GetDescendants())do
	if obj:IsA("BasePart")then
		obj.Anchored = false
	end
end
wait()
mass = require(script.MassGetter)()

Also, the vehicle is cloned and parented to the character using a script in StarterCharacterScripts.

Please do not ask people to write entire scripts or design entire systems for you. If you can’t answer the three questions above, you should probably pick a different category.

I know what I’m asking is beyond the scope of what most of you can answer, but if you need more details and resources, for example the WheelScript, I can provide them. If you want, I can also send the model of the car.

Probably (definitely) me being dumb, but is there a reason this is a LocalScript and not a ServerScript?

If you must know, I also used a Script, and the only problem is the lag. It works totally fine without any problems but controlling the car and looking at the car’s suspension makes it seem like it’s lagging behind. Especially with controlling the car, where the steering doesn’t handle well and seems like there’s a delay to inputs.