Bug with whispering in TextChatService

So a while back I had to manually port and rescript my chat tags into the new TextChatService. And since I didn’t know much about it, I followed the devforum post tutorial. It worked fine until a bug regarding system messages appeared, which I managed to fix quickly.

But now, there’s a bug with /whisper and I haven’t been really aware of how to actually fix it. I’m not really into touch with TextChatService (as I said), and it’s pretty hard to find a fix for this.

The bug simply just deletes the “To player” and “Whispering to” thing off the chat, which makes so only the bubble chat appears, and the other player can’t see it in chat.

local cwc = txt.ChatWindowConfiguration

function gradient(props, colors, rotation)
	local ui = Instance.new("UIGradient", props)
	ui.Color = colors
	ui.Rotation = rotation or 0
	return ui
end

function to_hex(color)
	return string.format("#%02X%02X%02X", color.R * 0xFF, color.G * 0xFF, color.B * 0xFF)
end

txt.OnChatWindowAdded = function(msg)
	if msg.Metadata and (msg.Metadata:lower():match("whisper") or msg.Metadata:lower():match("to ")) then
		return
	end
	
	local msgProps = cwc:DeriveNewMessageProperties()

	if msg.TextSource then
		local player = game:GetService("Players"):GetPlayerByUserId(msg.TextSource.UserId)
		if player and player:FindFirstChild("Tag") and player.Tag.Value then
			local tagText = ""
			local prefixHere = ""
			local hexC = player:FindFirstChild("ChatColor") and to_hex(player.ChatColor.Value.Color) or "#c7c7c7"
			
			msgProps.PrefixTextProperties = cwc:DeriveNewMessageProperties()

			if player.Tag.Value == "furry" then
				tagText = "%s"
				prefixHere = "[furry]"
				gradient(msgProps.PrefixTextProperties, ColorSequence.new(Color3.new(0, 0, 0), Color3.new(0.231373, 0.231373, 0.231373), Color3.new(0, 0, 0), Color3.new(0.231373, 0.231373, 0.231373), Color3.new(0, 0, 0), Color3.new(0.231373, 0.231373, 0.231373)), -90)
			elseif player.Tag.Value == "murder" then
				tagText = "<font color ='#FF0000'>%s</font>"
				prefixHere = "[COLD BLOODED MURDER]"
				msgProps.PrefixTextProperties.FontFace.Style = Enum.FontStyle.Italic
				gradient(msgProps.PrefixTextProperties, ColorSequence.new(Color3.fromRGB(85, 0, 0), Color3.fromRGB(220, 0, 0)), -90)
			elseif player.Tag.Value == "reaper" then
				tagText = "<font color ='#2B2B2B'>%s</font>"
				prefixHere = "[The Reaper]"
				gradient(msgProps.PrefixTextProperties, ColorSequence.new(Color3.fromRGB(204, 204, 204), Color3.fromRGB(15, 15, 15)), 90)
			elseif player.Tag.Value == "donator" then
				tagText = "<font color ='#FFFF00'>%s</font>"
				prefixHere = "[Donator]"
				gradient(msgProps.PrefixTextProperties, ColorSequence.new(Color3.new(1, 1, 0), Color3.fromRGB(181, 181, 0)), 45)
			elseif player.Tag.Value == "met" then
				tagText = "<font color ='#FFA500'>%s</font>"
				prefixHere = "[Met The Owner]"
				msgProps.PrefixTextProperties.FontFace.Style = Enum.FontStyle.Italic
				gradient(msgProps.PrefixTextProperties, ColorSequence.new(Color3.new(0, 0, 0), Color3.fromRGB(255, 85, 0)), 90)
			elseif player.Tag.Value == "herobrine" then
				tagText = "<font color ='#00BFFF'>%s</font>"
				prefixHere = "[Herobrine]"
				gradient(msgProps.PrefixTextProperties, ColorSequence.new(Color3.fromRGB(85, 170, 255), Color3.fromRGB(0, 85, 255)), 45)
			elseif player.Tag.Value == "moderator" then
				tagText = "<font color ='#0000FF'>%s</font>"
				prefixHere = "[Administrator]"
				local grad = gradient(msgProps.PrefixTextProperties, ColorSequence.new(Color3.fromRGB(0, 85, 255), Color3.fromRGB(0, 0, 127)), 90)
			elseif player.Tag.Value == "yapper" then
				tagText = "<font color ='#FF4500'>%s</font>"
				prefixHere = "[🔥🗣yapper🗣🔥]"
				gradient(msgProps.PrefixTextProperties, ColorSequence.new(Color3.new(1, 1, 0), Color3.new(1, 0.0666667, 0)), 90)
			elseif player.Tag.Value == "epic glitcher" then
				tagText = "<font color ='#FF0000'>%s</font>"
				prefixHere = "[epic glitcher]"
				gradient(msgProps.PrefixTextProperties, ColorSequence.new({ColorSequenceKeypoint.new(0, Color3.new(1, 0, 0)), ColorSequenceKeypoint.new(0.15, Color3.new(1, 0.333333, 0)), ColorSequenceKeypoint.new(0.3, Color3.new(1, 1, 0)), ColorSequenceKeypoint.new(0.45, Color3.new(0, 1, 0)), ColorSequenceKeypoint.new(0.6, Color3.new(0, 1, 1)), ColorSequenceKeypoint.new(0.75, Color3.new(0, 0, 1)), ColorSequenceKeypoint.new(1, Color3.new(0.666667, 0, 1))}), 0)
			elseif player.Tag.Value == "fan" then
				tagText = "<font color ='#FFFFFF'>%s</font>"
				prefixHere = "[Fan]"
				gradient(msgProps.PrefixTextProperties, ColorSequence.new(Color3.new(1, 1, 1), Color3.fromRGB(172, 172, 172)), -45)
			end
			if player.TeamColor and player.Team ~= nil and player.Team ~= "" then
				hexC = to_hex(player.TeamColor.Color)
			end
			if prefixHere ~= "" then
				msgProps.PrefixText = prefixHere
				msgProps.Text = string.format("<font color ='"..hexC.."'>%s</font>", msg.PrefixText).." "..msg.Text
			else
				msgProps.PrefixText = string.format("<font color ='"..hexC.."'>%s</font>", msg.PrefixText)
			end
		end
	elseif not msg.TextSource and msg.Metadata ~= "" and not msg.Metadata:match("Roblox.") then
		msgProps.PrefixText = string.format("<font color ='#%s'>%s: </font>", "FFFFFF", "<font color ='#c7c7c7'>"..msg.Metadata.."</font>")
	end

	return msgProps
end

Hey! I saw that you’re having an issue with /whisper messages not showing the “To player” or “Whispering to” parts in the chat. This usually happens when the custom message handling in OnChatWindowAdded accidentally filters or overrides whisper-related messages.

Here’s what might be causing the issue and how to fix it:

  1. Filtering messages with msg.Metadata
    At the beginning of your OnChatWindowAdded function, you have this:

    if msg.Metadata and (msg.Metadata:lower():match("whisper") or msg.Metadata:lower():match("to ")) then
        return
    end
    

    This line is likely blocking whisper messages from being processed properly. Your goal might have been to skip system messages, but in this case, you’re also skipping the whisper UI behavior.

    :point_right: Suggestion: Try removing or handling that condition differently. For example:

    if msg.Metadata and msg.Metadata:lower():match("whisper") then
        return nil -- Let the default system handle it
    end
    
  2. Check if msg.TextSource exists
    Sometimes in whisper messages, TextSource might be present only partially or not at all, depending on the message direction. You can log these to debug:

    print("Metadata:", msg.Metadata)
    print("PrefixText:", msg.PrefixText)
    print("Text:", msg.Text)
    

    This can help you understand what kind of data whisper messages contain.

  3. System messages rely on Metadata
    The whisper system uses special Metadata like "FromUserId" or "ToUserId" to display “Whispering to” and other tags. If your code is overwriting msgProps.PrefixText or msgProps.Text, the system can’t render the whisper formatting.

    :point_right: Make sure you preserve the original structure of messages with whisper-related metadata or let the system handle them entirely.

Final Thoughts

You don’t need to rewrite your whole customization logic — just make sure that whisper messages are either left untouched or carefully handled. If you ever want to fully customize whisper messages, you’ll need to dig deeper into how TextChatMessage.Metadata is structured and expected by the new TextChatService.