Failure[] objects in Wolfram Language may contain arguments that change for each iteration, so FixedPoint will never return:

In[1]:=

Clear[remove];

remove[LeafNode[tag_, s_String /; StringContainsQ[s, "a"], data_]] := LeafNode[tag, StringReplace[s, "a" -> ""], data]

remove[args___] := Failure["Bad!!", <|"Arguments" -> {args}|>]

In[2]:= TimeConstrained[FixedPoint[remove, LeafNode[foo, "aaab", <||>]], 0.3, $TimeConstrainedReturned]

Out[2]= $TimeConstrainedReturned

In the above example, remove[LeafNode[foo, "b", <||>]] is eventually called, and Failure["Bad!!", <|"Arguments" -> {LeafNode[foo, "b", <||>]}|>] is returned.

And then remove[Failure["Bad!!", <|"Arguments" -> {LeafNode[foo, "b", <||>]}|>]] is called, and Failure["Bad!!", <|"Arguments" -> {Failure["Bad!!", <|"Arguments" -> {LeafNode[foo, "b", <||>]}|>]}|>] is returned.

And so on.

This is in infinite regress, and FixedPoint will never return.

It would be better to simply return $Failed in this case, so that FixedPoint may return:

In[1]:=

Clear[remove];

remove[LeafNode[tag_, s_String /; StringContainsQ[s, "a"], data_]] := LeafNode[tag, StringReplace[s, "a" -> ""], data]

remove[___] := $Failed

In[2]:= FixedPoint[remove, LeafNode[foo, "aaab", <||>]]

Out[2]= $Failed

Updated: