I’m trying to position a knife on the player’s torso. In the command line I’ve written this and for some reason the x and z axis are rotating on the same axis.

local knife = workspace.Knife
local torso = workspace.Dummy.UpperTorso
knife.CFrame = torso.CFrame * CFrame.Angles(math.rad(0), math.rad(90), math.rad(0))

Yes, this is a well known problem. Euler angles (the standard XYZ angles you’re familiar with) have infinite solutions for a given orientation. Usually this results in “Gimbal lock.”

You cannot use XYZ angles in refined cases like this. You must use a matrix or an axis angle.

Rotations [for .Angles] are applied in Z, Y, X order.
—CFrame

So in the first, you’re rotating about Z by 45 and then about Y by 90.

In the second, you rotate about 90 on Y and then X by 45.

Both of those end up the same.

Solution:

You can explicitly control which one gets multiplied first:

-- force rotate on Z before Y (this is the default but we can make it explicit)
torso.CFrame * CFrame.Angles(0, 0, math.rad(45)) * CFrame.Angles(0, math.rad(90), 0)
-- force rotate on X before Y
torso.CFrame * CFrame.Angles(math.rad(45), 0, 0) * CFrame.Angles(0, math.rad(90), 0)

Or use fromOrientation/fromEulerAnglesYXZ, which applies rotations in Y, X, Z order.

-- always rotate Y first and then X or Z so these give different answers
knife.CFrame = torso.CFrame * CFrame.fromOrientation(0, math.rad(90), math.rad(45))
knife.CFrame = torso.CFrame * CFrame.fromOrientation(math.rad(45), math.rad(90), 0)