Serguey Asael Shinder: replaceAll treats the dollar sign and backslash in the replacement as special too
Most people know that the first argument of String.replaceAll is a regular expression and quote it when the search text is literal. Fewer remember that the second argument is not literal either.
String template = "Total: AMOUNT";
String price = "$5";
template.replaceAll("AMOUNT", price);
// throws IndexOutOfBoundsException: there is no group 5
Why. The String.replaceAll javadoc) says the call is exactly Pattern.compile(regex).matcher(str).replaceAll(repl), and warns: "Note that backslashes (\\) and dollar signs ($) in the replacement string may cause the results to be different than if it were being treated as a literal replacement string."
In the replacement, Matcher treats $g and ${name} as references to captured groups, and \ as an escape. If the reference points at a group the pattern does not have, appendReplacement throws: IndexOutOfBoundsException for a numbered group, IllegalArgumentException for a named one. If the group does exist, nothing is thrown, and you get the wrong text instead.

Where it bites. The replacement usually comes from data, not from the code: a price with a currency sign, a Windows path, a password, a user's display name. Tests pass with plain strings; the first value with a $ or \ fails in production.
The fixes, by intent.
- The search text is literal too: use
String.replace(CharSequence, CharSequence), which replaces "the literal target sequence with the specified literal replacement sequence". No regex on either side. - You need a regex, but the replacement is data: wrap it with
Matcher.quoteReplacement), which returns a string in which "Slashes ('\\') and dollar signs ('$') will be given no special meaning".
template.replace("AMOUNT", price); // literal both ways
template.replaceAll("AMO+UNT", Matcher.quoteReplacement(price)); // regex, literal value
The same applies to replaceFirst and to Matcher.replaceAll and appendReplacement called directly.
No JDK runs on the machine this note was written on; the behaviour described is the one specified in the linked javadoc.