When given a url like /api/%255E/users.json, RequestPreprocessor#preprocess first does handleExtensionMappings, which creates a PathSegmentImpl with a single arg constructor, decoding the path. The path on Message is set to the decoded path with a call to updatePath if the .json extension was found within the extension mappings. Back in RequestPreprocessor#preprocess, the path returned will be decoded again within the call to UriInfoImpl#getPath.
This causes the %255E in the path to be decoded the first time as %5E, then the second time as ^.
I believe this could be fixed by constructing PathSegment within handleLanguageMappings and handleExtensionMappings with a second false argument.