UTF-8 is correct 95% of the time
Modern files are UTF-8. Modern web protocols are UTF-8. Modern source code is UTF-8. Unless you have a specific reason to use something else (legacy data, system constraints), UTF-8 is the right default. The remaining 5% — Windows legacy files, Korean Windows tools that emit CP949, old Excel exports — are real and you'll meet them, but they're the exception.
The BOM — and why it exists
A byte-order mark (BOM) is a special character at the start of a file that signals the encoding. UTF-8 doesn't really need one (the encoding is unambiguous), but Windows software often writes it anyway. utf-8-sig as the encoding when reading skips the BOM if present; utf-8 would leave it as a literal character at the start of your data.
What goes wrong — and how to read the symptoms
UnicodeDecodeError means you're trying to decode bytes that aren't valid in the encoding you specified. Common causes: a file is actually UTF-16 or CP1252 and you tried UTF-8; the file is binary and you tried text mode; a multi-byte character was cut in half. Read the error: it tells you the byte position and what byte was problematic.
chardet — when you really don't know
pip install chardet gives you a library that guesses encodings from a byte sample. Useful for one-time imports of mystery files. Don't put it in production paths — the guess is a probability, not a guarantee.
encoding when opening text files. The system default is platform-specific and will bite cross-platform code. encoding="utf-8" if you control both ends. Match the actual encoding if you don't.
Needing to guess an encoding means the boundary lacks a contract. Treat detector output as a candidate, preserve irreplaceable original bytes, and verify representative text after conversion.
errors='ignore' is silent loss, not recovery. Even when replacement characters are acceptable, record which file and byte position failed so the converted copy can be checked against the original.