Fuuuck , I want that cherry covered cheesecake super bad right now . <| sep |> One reason is that Py 3 by default insists on decoding input that it actually can ' t , and silently hands your code borked str objects containing surrogate escapes . In Py 2 , when you create a unicode object , it will either be valid , encodable Unicode or you ' ll get an exception . In Py 3 , several APIs by default may return str objects containing surrogate escapes that will explode if you try to encode them . And Py 3 simply doesn ' t give you the tools you need to handle them . Such str objects aren ' t marked in any way , nor are they tagged with whichever encoding Py 3 used to semidecode them . As a result , it ' s up to the developer to know which APIs may return surrogate escapes and which encoding they use , and try to deal with them there . So while Py 3 ' s behaviour of throwing an exception when you try to mix bytes and Unicode strings is an improvement over Py 2 ' s behaviour of trying to promote the bytes to Unicode , Py 3 ' s Unicode implementation suffers from a very big issue that Py 2 ' s doesn ' t have .