Internal Luau error: Free types leaked into this module's public interface

Studio told me to report this. Put the following into a modulescript and observe that studio complains:

--!strict
local module = {}

--
-- bezier curve
function module.newBezier()
	return {
		points = {},
		GetPointAtAlpha = function(self, alpha: number) : Vector3
			local set = {unpack(self.points)}
			local nset = #set
			while nset > 2 do
				local subset = {}
				for i=1, #set-1 do
					subset[i] = set[i]:Lerp(set[i-1], alpha)
				end
				set = subset
				nset -= 1
			end
			return set[1]:Lerp(set[2], alpha)
		end
	}
end
export type BezierCurve = typeof(module.newBezier())

--
-- spline curve
function module.newSpline()
	return {
		points = {},
		pointVectors = {},
		nodeVectors = {},
		k = 1,
		open = false
	}
end
export type SplineCurve = typeof(module.newSpline())

return module
4 Likes

Thanks for the report! We’ve filed a ticket to our internal database and we’ll follow up when we have an update for you.

2 Likes

I found some code a few months ago that produces the same error:

--!strict

local object = {}
object.__index = object

type Predicate = (any) -> boolean

function object.new(a: Predicate?)
	local self = setmetatable({}, object)
	self.compare = a or function(obj)
		return true
	end
	return self
end

export type object_Type = typeof(object.new())
return object

Just received this error while working on a pull request for MorphModule.

Free types leak code
-- Strict mode will not work on this code due to an internal Luau error

local CollectionService: CollectionService = game:GetService("CollectionService")

local MORPH_OBJECT_TAG_NAME: string = "MorphModuleObject"

local Morph = {}
Morph.__index = Morph

local function clearCharacterAppearance(character: Model, doAccessories: boolean?, doClothes: boolean?)
	if not character then
		warn(debug.traceback("Cannot clear character appearance: no model specified"))
		return
	elseif not doAccessories and not doClothes then
		warn(debug.traceback("No appearance items to clear"))
		return
	end

	local humanoid: Instance? = character:FindFirstChildOfClass("Humanoid")

	if doAccessories and humanoid then
		(humanoid :: Humanoid):RemoveAccessories()
	end

	if doClothes then
		for _: number, object: Instance in ipairs((character :: Model):GetChildren()) do
			if object:IsA("Clothing") then
				object:Destroy()
			elseif doAccessories and object:IsA("Accessory") then
				-- Take advantage of the loop to remove accessories that couldn't be removed
				-- if a Humanoid didn't exist to call RemoveAccessories on
				object:Destroy()
			end
		end
	end
end

function Morph.new(folder: Folder)
	return setmetatable({
		Folder = folder
	}, Morph)
end

function Morph:SeekFolder(folderName: string): Folder?
	for _, object in ipairs(self.Folder:GetDescendants()) do
		if object:IsA("Folder") and object.Name == folderName then
			return object
		end
	end
	return nil
end

function Morph:ApplyFullMorph(player: Player, folderName: string, includeClothes: boolean?)
	local character: Model? = player.Character
	local humanoid: Humanoid
	local selectedFolder: Folder = self:SeekFolder(folderName)
	
	if not selectedFolder then
		warn(debug.traceback(string.format("No morph named %s exists", folderName)))
		return
	elseif not character then
		warn(debug.traceback(string.format("%s does not have a character", player.Name)))
		return
	else
		humanoid = character:FindFirstChildOfClass("Humanoid") :: Humanoid
		if not humanoid then
			warn(debug.traceback(string.format("%s does not have a Humanoid", player.Name)))
			return
		end
	end

	clearCharacterAppearance((character :: Model), true, includeClothes)

	for _: number, item: Instance in ipairs(selectedFolder:GetChildren()) do
		if item:IsA("Accessory") then
			local newAccessory = item:Clone()
			CollectionService:AddTag(newAccessory, MORPH_OBJECT_TAG_NAME)
			humanoid:AddAccessory(newAccessory)
		elseif includeClothes and item:IsA("Clothing") then
			local newClothing = item:Clone()
			CollectionService:AddTag(newClothing, MORPH_OBJECT_TAG_NAME)
			newClothing.Parent = character
		end
	end
end

function Morph:ApplyMorph(player: Player, folderName: string)
	self:ApplyFullMorph(player, folderName, false)
end

function Morph:RemoveFullMorph(player: Player, doClothes: boolean?)
	local character: Model? = player.Character

	if not character then
		warn(debug.traceback(string.format("%s does not have a character", player.Name)))
	end

	for _: number, item: Instance in ipairs((character :: Model):GetChildren()) do
		if item:IsA("Accessory") and CollectionService:HasTag(item, MORPH_OBJECT_TAG_NAME) then
			item:Destroy()
		elseif doClothes and item:IsA("Clothing") and CollectionService:HasTag(item, MORPH_OBJECT_TAG_NAME) then
			item:Destroy()
		end
	end
end

function Morph:RemoveMorph(player: Player)
	self:RemoveFullMorph(player, false)
end

print("MorphModule v1 fully loaded!")

return Morph

EDIT 07/02/2021 @ 10:35 PM EST: Apparently I resolved this issue? Not getting the flag anymore. Might be worth comparing the code sample I changed to.

Code that no longer leaks free types
local CollectionService: CollectionService = game:GetService("CollectionService")

local MORPH_OBJECT_TAG_NAME: string = "MorphModuleObject"

local Constructors = {}

local Morph = {}
Morph.__index = Morph

local function clearCharacterAppearance(character: Model, doAccessories: boolean?, doClothes: boolean?)
	if not character then
		warn(debug.traceback("Cannot clear character appearance: no model specified"))
		return
	elseif not doAccessories and not doClothes then
		warn(debug.traceback("No appearance items to clear"))
		return
	end

	local humanoid: Instance? = character:FindFirstChildOfClass("Humanoid")

	if doAccessories and humanoid then
		(humanoid :: Humanoid):RemoveAccessories()
	end

	if doClothes then
		for _: number, object: Instance in ipairs((character :: Model):GetChildren()) do
			if object:IsA("Clothing") then
				object:Destroy()
			elseif doAccessories and object:IsA("Accessory") then
				-- Take advantage of the loop to remove accessories that couldn't be removed
				-- if a Humanoid didn't exist to call RemoveAccessories on
				object:Destroy()
			end
		end
	end
end

-- Required properties like Folder will be nil at a class-level
-- Expose constructors instead to work with morphs
function Constructors.new(folder: Folder)
	if not folder then
		warn(debug.traceback("No folder specified; no object created from constructor"))
		return
	end

	return setmetatable({
		Folder = folder
	}, Morph)
end

function Morph:_SeekFolder(folderName: string): Folder?
	if not folderName then
		-- Traceback provided by function calling _SeekFolder
		return nil
	end

	for _, object in ipairs(self.Folder:GetDescendants()) do
		if object:IsA("Folder") and object.Name == folderName then
			return object
		end
	end
	return nil
end

function Morph:ApplyFullMorph(player: Player, folderName: string, includeClothes: boolean?)
	local character: Model
	local humanoid: Humanoid
	local selectedFolder: Folder = self:_SeekFolder(folderName)

	if not selectedFolder then
		warn(debug.traceback(string.format("No morph named %s exists", folderName)))
		return
	elseif not player then
		warn(debug.traceback("No player specified"))
		return
	elseif not character then
		character = player.Character :: Model
		if not character then
			warn(debug.traceback(string.format("%s does not have a character", player.Name)))
			return
		end
	else
		humanoid = character:FindFirstChildOfClass("Humanoid") :: Humanoid
		if not humanoid then
			warn(debug.traceback(string.format("%s does not have a Humanoid", player.Name)))
			return
		end
	end

	clearCharacterAppearance((character :: Model), true, includeClothes)

	for _: number, item: Instance in ipairs(selectedFolder:GetChildren()) do
		if item:IsA("Accessory") then
			local newAccessory = item:Clone()
			CollectionService:AddTag(newAccessory, MORPH_OBJECT_TAG_NAME)
			humanoid:AddAccessory(newAccessory)
		elseif includeClothes and item:IsA("Clothing") then
			local newClothing = item:Clone()
			CollectionService:AddTag(newClothing, MORPH_OBJECT_TAG_NAME)
			newClothing.Parent = character
		end
	end
end

function Morph:ApplyMorph(player: Player, folderName: string)
	self:ApplyFullMorph(player, folderName, false)
end

function Morph:RemoveFullMorph(player: Player, doClothes: boolean?)
	local character: Model? = player.Character

	if not character then
		warn(debug.traceback(string.format("%s does not have a character", player.Name)))
	end

	for _: number, item: Instance in ipairs((character :: Model):GetChildren()) do
		if item:IsA("Accessory") and CollectionService:HasTag(item, MORPH_OBJECT_TAG_NAME) then
			item:Destroy()
		elseif doClothes and item:IsA("Clothing") and CollectionService:HasTag(item, MORPH_OBJECT_TAG_NAME) then
			item:Destroy()
		end
	end
end

function Morph:RemoveMorph(player: Player)
	self:RemoveFullMorph(player, false)
end

function Morph:Destroy()
	self.Folder = nil
	setmetatable(self, nil)
end

print("MorphModule v1 fully loaded!")

return Constructors

This also happened to me when you return an array with a metatable, returning anything else fixes the error.

I have the Luau type-checking beta test enabled.

image