if the ivy.retrieve.pattern contains an optional parameter as the first parameter, the sync feature of ivy:retrieve will get a path with a trailing '(' causing it to find nothing to sync, leaving old files still in the retrieve path.
ivy.retrieve.pattern = lib/([type]/)/[artifact].[ext]
sync will try to match orphaned files against lib/( which obviously contains nothing.
The problem is in IvyPatternHelper.java getTokenRoot(), which only checks for the first '[', but in the pattern above it needs to check for the first '('.
I could see that nothing but the Resolver uses this function so it looks like it'll be pretty safe to just fix the check. I've attached a suggested fix below. I rarely touch java, so there might be simpler ways to do it, but at least it works.