DriversRecommendedOutdated drivers can make a good PC feel brokenScan driver issues before chasing fixes manually.Scan NowHalloween DealsAmazon USHalloween week deals: useful picks before month-endAmazon US: current deals, useful picks and tech finds.Check DealsWindows FixRecommendedWindows errors stealing your time? Find the fix fastScan stability, cleanup and performance issues.Fix Now×
FREEDOM251

Freedom251 › Blog › PEP 597

Fix UnicodeDecodeError and UnicodeEncodeError After Upgrading to Python 3.15

By Freedom251 Team · · 12 min read

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Python 3.15 makes UTF-8 mode the default. Code that omitted an encoding and quietly inherited the operating system locale can therefore start raising UnicodeDecodeError, UnicodeEncodeError, mojibake, or even silently altered data.

The durable fix is to define the encoding at every bytes/text boundary. Use UTF-8 when the producer’s contract is UTF-8, use encoding='locale' only when the data is intentionally in the current locale encoding, and test the output destination as carefully as the input.

As an Amazon Associate I earn from qualifying purchases.

Why these errors appeared after the Python 3.15 upgrade

Python 3.15.0 was released on October 9, 2026. Under PEP 686, UTF-8 mode is enabled by default instead of being an opt-in mode. As the Python 3.15 documentation explains, I/O operations without an explicit encoding, such as open('flying-circus.txt'), now use UTF-8.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Before 3.15, an omitted encoding commonly meant “use the platform locale.” That could mean a Windows code page, a Unix locale, or another environment-dependent codec. The same source code could consequently read the same byte sequence differently on two machines. Python 3.15 makes the default more consistent, but it also exposes programs that accidentally depended on the old default.

#1 Best Overall
Sale
TOZO A1 Wireless Earbuds Bluetooth 5.3 Light Weight in Ear Headphones | IPX5 Waterproof,2 Mics for AI Calls,Immersive Premium Sound Bass Headset with Charging Case,32 EQ via App Black
  • [Ultra-Lightweight Ear Buds Designed for Small Ears] Each earbud weighs only 3.7g and features a compact, ergonomic in-ear design made especially for small ears. Secure, low-profile, and comfortable for workouts, all-day wear or overnight sleeping.The ultra-slim profile sits flush in your ear with no hard edges poking or rubbing, so you can even sleep on your side without discomfort.
  • [Immersive Stereo Sound with TOZO OrigX Technology] TOZO OrigX tuning delivers clear vocals, balanced mids, and natural stereo sound for music, podcasts, and videos.
  • [Long Battery Life for Daily Use] Get up to 7 hours of playtime on a single charge, with up to 32 hours total using the charging case—ideal for workdays, commuting, and extended listening sessions.
  • [Bluetooth 5.3 & Stable Connection] Bluetooth 5.3 provides fast pairing, stable wireless performance, and reduced dropouts as you move around home or office.
  • [Deep Bass with Clear Vocals] High-performance drivers produce punchy bass while keeping vocals clean and detailed for everyday listening.

PEP 686’s author, Inada Naoki, states: “Python will enable UTF-8 mode by default from Python 3.15.” The change affects calls that leave encoding unspecified; it does not determine the correct encoding for every existing file, protocol, database export, or command-line stream. The Python Software Foundation’s Python 3.15.0 release page records 5,643 commits and 1,012 contributors, but the migration issue relevant here is specifically the default text encoding.

Three outcomes are possible when an implicit-encoding assumption is exposed:

  • UnicodeDecodeError: Python cannot turn incoming bytes into Unicode text using the selected codec.
  • UnicodeEncodeError: Python has a valid Unicode string but cannot represent it in the destination codec.
  • Wrong text without an exception: the bytes are technically valid in the chosen codec but represent the wrong characters, producing mojibake or persistent data corruption.

Python’s codecs use strict error handling by default. That is why an invalid byte or unrepresentable character normally raises an exception instead of being discarded.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

First identify which boundary is failing

Do not begin by changing every encoding setting in the application. Find the exact conversion that failed and classify its direction. The traceback usually shows the operation, codec, and offending byte or character.

Boundary Conversion Typical operation Likely repair
File, HTTP response, database export, or subprocess bytes entering Python Bytes to text (decode) open() read, response.content.decode(), stdout.decode() Use the encoding promised by the producer, explicitly.
File, pipe, terminal, socket, or report leaving Python Text to bytes (encode) open() write, stdout.write(), subprocess input, protocol serialization Choose a destination codec that can represent the text and that the consumer accepts.
Implicit text I/O Either direction open(path), Path.read_text(), CSV helpers, text-mode subprocesses Supply an explicit encoding instead of inheriting the locale or runtime default.

Reading a file: diagnose the producer first

Ask how the file was created. A UTF-8 export should be read as UTF-8; a deliberately local-language file should be read with its documented legacy codec or the current locale. “It works on my machine” is evidence of an environment dependency, not proof of the file’s encoding.

from pathlib import Path

text = Path('customers.csv').read_text(encoding='utf-8')

For a file object, make the same contract explicit:

with open('customers.csv', encoding='utf-8', newline='') as f:
    rows = f.read()

Use newline='' when handing a text file to Python’s CSV module so that the CSV reader or writer can handle newline conventions itself.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Writing a file: diagnose the consumer

A Unicode string can be perfectly valid and still fail when written to an ASCII or other restricted destination. If another program expects UTF-8, write UTF-8 explicitly:

Rank #2
Sale
Smart Watch for Men Women Compatible with iPhone Samsung Android Phone 1.69 inch Full Touch Screen IP68 Waterproof Bluetooh Fitness Tracker Heart Rate/Sleep Monitor (Black)
  • 【IP68 Waterproof Multifunctinal Smart Watch】Torjalph fitness activity tracker smart watch is IP68 waterproof technology ,you can wear it when swimming,handwashing,it's rain-proof which means that you perform multiple exercise modes outside without worrying about sweat or rain corroding your fitness smart watch,and this watch equipped with a 230 mAh large capacity battery magnetic charging ,and this sports activity tracker watch can be used 5-7 days If fully charged.
  • 【Larger 1.83'' Full Screen Watch & Multi-dial Selection】1.83 inch full HD color touch screen smart watch for men / women provides excellent 240*284 resolution image quality ,This Bluetooth smart watch offers a better touch experience and remarkable immersion and ,more than 200 watch faces for you to choose from App"Da Fit ", you can change the dial style according to your mood to wear keeping the world on your wrist
  • 【Accurate Activity Tracker Smart Watch 】sports watch activity tracker uses sweat and dust resistant breathable silicone strap, our Fitness Tracker Smart Watch for Android iOS Phones supports 12 sports modes,walking,running,bicycle,swimming,badminton,basketball,football,rope skipping. This bluetooth calls smart watch for men women is lightweight and stylish, bringing more convenient and relaxing sports experience while also providing more protection.
  • 【Your 24 Hour Personal Daily Health Monitor】Fitness watch for women men full touch screen IP67 waterproof bluetooth smartwatch for Android iOS Phones with heart rate blood pressure monitor multifunction Andriod smart watch with steps counter sleep monitor sports fitness watch, monitoring blood oxygen saturation allows you to understand your physical condition in real time to track fitness progress ,to know your health status in time.
  • 【More Functions Bluetooth Call Smart Watch】This fitness activity smart watch full screen for iPhone and Android phones includes a bunch of new features including:The bluetooth smart watch can hang up the phone, receive and display WhatsAPP /Text /Facebook /Twitter /Messenger reminder.Support sedentary reminder, alarm clock, stopwatch, timer,weather information,music control, remote camera control compatible with Android 5.0 and above or iOS 9.0 and above smartphones.
with open('report.txt', 'w', encoding='utf-8', newline='') as f:
    f.write(report)

If the receiving system requires a particular regional codec, use that codec only when its interface contract says so. Do not switch to a lossy codec merely to suppress an exception.

Subprocesses and pipes

Text mode in subprocess performs encoding and decoding for you, so it also needs an explicit contract. For a tool documented to emit UTF-8:

import subprocess

result = subprocess.run(
    ['tool', '--export'],
    capture_output=True,
    text=True,
    encoding='utf-8',
    errors='strict',
    check=True,
)

Apply the same rule to input=, stdin, stdout, and stderr. If the child process uses a locale-dependent encoding, document that fact and select encoding='locale' or the named codec required by that process.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

HTTP and other protocol payloads

Network data arrives as bytes. Decode it according to the protocol’s declared charset, not according to the machine running your Python process. For a payload whose contract is UTF-8:

body = response.content.decode('utf-8', errors='strict')

If a protocol supplies a different charset, honor that declaration. If it supplies no reliable declaration, investigate the producer before choosing a codec; there is no universal fallback that can recover unknown original bytes without risk.

Make every text-I/O contract explicit

Once the failing boundary is known, remove implicit defaults at that boundary and at nearby helper calls. Python’s migration guidance is: “For best compatibility between versions of Python, ensure that an explicit encoding argument is always provided.”

Built-in and path-based file APIs

open(path, encoding='utf-8')
Path(path).open('r', encoding='utf-8')
Path(path).read_text(encoding='utf-8')
Path(path).write_text(text, encoding='utf-8')

For a locale-defined file, replace 'utf-8' with 'locale'. That spelling asks Python to use the current locale encoding explicitly, even though UTF-8 mode is active. It is appropriate only when the file is intentionally tied to that locale.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

CSV, JSON, and generated reports

Pass the encoding when opening the underlying stream; helpers inherit the stream’s behavior. A UTF-8 CSV export should therefore look like:

Rank #3
New Bee Guitar Cable 10ft Electric Instrument Bass AMP Cord | 6.35mm Jack, Durable Design, Stage and Home Studio Use, Low Noise Shielded Electric Cord for Bass, Keyboard, Amp, Mixer
  • [Low Noise High Fidelity] New bee guitar cord with 22AWG oxygen-free-copper center conductors provides enhanced signal transfer, the PVC insulation material and the high-density stranded copper double shield provide noise rejection for quiet operation
  • [Ideal for Choice] New bee 1/4 straight and right angle metal connectors, ensures a secure connection in any instrument, pedal, or amp jack. L shape great fro on stage guitar stands
  • [Wide Compatible] Suitable for mixer, power amplifier, microphone, effector, decoder, equalizer, electoronic organ, electric guitar, electric bass and other electronic equipments. Widely used in the installation of smart home, karaoke speaker, professional stage audio, etc
  • [Easy Disassemble] Making incidental repairs quick and easy! Very nice, flexible cable
  • [Dont Hesite to Order] 24 hours customer services and professional technology team are standing by
import csv

with open('export.csv', 'w', encoding='utf-8', newline='') as f:
    writer = csv.writer(f)
    writer.writerow(['name', 'city'])
    writer.writerow(['Zoë', 'München'])

For JSON, the file handle still determines the encoding:

import json

with open('settings.json', 'w', encoding='utf-8') as f:
    json.dump(settings, f, ensure_ascii=False, indent=2)

ensure_ascii=False preserves non-ASCII characters in the UTF-8 file. It does not choose the file encoding; open() does that.

Standard output and redirected streams

Terminal output can fail even when file handling is correct. The terminal, a redirected file, or a consuming process may accept a narrower encoding than your string. When you control the stream and know it accepts UTF-8, you can configure it:

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import sys

sys.stdout.reconfigure(encoding='utf-8', errors='strict')
print('Résumé – 東京')

This does not make every downstream terminal UTF-8-capable. A Windows console, a CI log collector, or a pipe connected to another program may impose its own contract. Fix the consumer or choose a codec it explicitly supports.

How to force Python to use UTF-8

Forcing UTF-8 is useful when the application’s entire external contract is UTF-8, or as a temporary compatibility measure while you remove implicit calls. It is not a substitute for identifying files and protocols that genuinely use another encoding.

Process-wide UTF-8 mode

Use the command-line switch for one process:

python -X utf8 app.py

On POSIX shells, the environment variable form is:

PYTHONUTF8=1 python app.py

In PowerShell:

$env:PYTHONUTF8 = '1'
py app.py

These controls affect Python’s default text-I/O behavior for that process. They do not transcode an existing file, correct bytes that were already mis-decoded, or override an explicit encoding= argument.

Temporarily restore the pre-3.15 default

During a staged deployment, set PYTHONUTF8=0 or run:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
python -X utf8=0 app.py

This compatibility control can keep an older locale-dependent application running while you deploy explicit encoding fixes. Treat it as temporary: it preserves the very environment dependence that Python 3.15 is designed to expose, so test the repaired application with UTF-8 mode enabled before removing the control.

Rank #4
BSIMB 15.6" Digital Family Calendar & Chore Chart, No Subscription Fees
  • Digital Family Calendar: BSIMB digital calendar wall touch screen puts an end to your chaotic life. Designed for busy families, the digital calendar keeps you organised and in step with your family. Assign different colours and tasks to each family member to make family scheduling easier and more intuitive.
  • Easy Setup and Auto-Sync: Plug in, connect to WLAN and auto-syn your electric calendar in minutes. Digital wall calendar compatible with Google calendar, iCloud calendar, Outlook calendar, Cozi calendar and Yahoo calendar. Add events directly on the device or through the ecalendar app. Electronic calendar stays current whether you're at work or traveling—no manual updates needed.
  • All-In-One Home Assistant: Smart calendar builds habits and encourage independence with personalised chores. Display daily meal plan to reduce the daily ‘What's for dinner?’ stress. To-do lists keep track of everything you want. Turning chores into valuable achievements with stars and rewards, making life easier and more enjoyable.
  • Manage Anytime Anywhere: BSIMB electronic calendar for family stay connected with the powerful ecalendar app. Access and manage your digital wall calendar anytime, anywhere with free features like stars & rewards, photo screensaver, chore charts, meals plans, to-do lists etc. Electronic planner paid features like Magic Import tailor advanced tools for users with special needs.
  • Stay in Touch with Loved Ones: Share photos or videos from your phone to electronic wall calendar with app, instantly update your life moment, and keep in touch with your family, especially those who are far away; you can also surprise your smart calendar digital wall by sending him a wish card; it's also great for keeping tech-illiterate elders in touch with the youngsters.

Confirm which mode a process is using

import sys

print(sys.flags.utf8_mode)

A value of 1 indicates UTF-8 mode is active. This is useful in startup diagnostics, but logging the mode does not replace an encoding contract for a particular file or protocol.

Choosing encoding='utf-8' or encoding='locale'

The correct choice comes from the producer and consumer agreement. Python cannot infer the original encoding reliably from arbitrary bytes, and no single legacy encoding is correct for every application.

Choice Use it when Data fidelity Portability Reversibility Operational scope
encoding='utf-8' The file, protocol, or tool documents UTF-8. Lossless for valid UTF-8; invalid bytes raise under strict. High: the same codec behaves consistently across operating systems. High when the original data is valid UTF-8. One call or interface; usually the safest targeted fix.
encoding='locale' The data is intentionally created for the current machine or user locale. Can be lossless for that locale; may fail elsewhere. Low to medium because locale settings vary. Depends on the locale codec and whether it can represent every character. One call that explicitly preserves locale behavior.
PYTHONUTF8=1 or -X utf8 The whole process has UTF-8 external contracts, or you are testing migration. Lossless only where the inputs really are UTF-8. High for UTF-8 interfaces, but it can expose non-UTF-8 legacy data. High for valid UTF-8; no recovery of incorrectly decoded text. Process-wide switch.
PYTHONUTF8=0 or -X utf8=0 Temporary rollback while fixes are deployed. Preserves old behavior, including its risks. Low because results depend on locale. Only as reliable as the previous locale contract. Process-wide compatibility control.

If a vendor specification says “Windows-1252,” use that named codec rather than guessing between UTF-8 and locale. If the specification is missing, inspect the producing application, export settings, protocol headers, or a representative byte sample and then record the decision in code and documentation.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Handle errors without hiding corruption

The default errors='strict' behavior is usually the right production setting because it stops at the first contract violation. It tells you that the selected codec and the incoming or outgoing data disagree.

When replace is acceptable

errors='replace' substitutes replacement characters or fallback markers. This can keep a user-facing log readable when exact text is not essential, but the lost character cannot be reconstructed from the output. Document the loss and keep the original bytes if later recovery matters.

text = raw.decode('utf-8', errors='replace')

When ignore is dangerous

errors='ignore' drops undecodable or unencodable data. It can silently change names, identifiers, source code, or financial text and should be reserved for a clearly documented lossy display path, never for archival or interchange data.

Preserving undecodable bytes for a controlled round trip

For some operating-system interfaces, errors='surrogateescape' preserves otherwise undecodable bytes in private surrogate code points so they can be encoded back with the same error handler. This is an advanced boundary technique, not a replacement for discovering the real codec; do not pass surrogate-containing text to systems that expect ordinary Unicode.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Use EncodingWarning to find hidden implicit encodings

PEP 597 defines an optional warning for code that relies on the default encoding. Run your test suite with:

python -X warn_default_encoding -m pytest

You can also enable it through the PYTHONWARNDEFAULTENCODING environment variable. The warning points to calls such as open() without an encoding, allowing you to replace them systematically instead of waiting for a particular machine or customer file to fail.

Search application code, tests, plugins, and utility modules for:

  • open(, Path.open(, read_text(, and write_text( without an encoding;
  • CSV or report code that opens a stream indirectly;
  • subprocess calls using text=True or universal_newlines=True without encoding=;
  • manual .decode() or .encode() calls that rely on a default or an unspecified protocol charset;
  • logging, archive, and temporary-file helpers whose encoding is selected by a library default.

Third-party libraries may still make implicit choices internally. Upgrade them when a release documents Python 3.15 compatibility, configure their encoding option when available, and isolate any unavoidable locale-dependent boundary behind a small adapter.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Common failure patterns and precise fixes

“The file worked on my old laptop”

The old machine probably supplied a locale codec that happened to match the file. Identify the export application and change the read call to that codec. If the file is intended to be portable, convert it once to UTF-8 with a verified tool and make future exports UTF-8 rather than relying on each reader’s locale.

“Reading succeeds, but writing the same text fails”

Reading and writing may use different implicit destinations. The text was decoded successfully, but the output stream cannot represent a character such as an em dash or an accented name. Set the destination encoding explicitly and verify the receiving program’s support. Do not assume that a successful decode guarantees a successful encode.

“The subprocess output is garbled, not rejected”

Garbled output means the selected codec may be decoding every byte into valid but incorrect characters. Check the child program’s documentation and locale. Capture raw bytes first when necessary, then decode once with the documented codec. Avoid decoding the same output twice through different defaults.

“The terminal raises an ASCII encoding error”

The destination stream, not your Python string, is the limitation. Check whether output is interactive, redirected, or collected by a CI service. Configure the stream to UTF-8 when the consumer supports it, or emit a documented fallback representation for a deliberately restricted channel. Keep full-fidelity text in a UTF-8 file or structured log if the terminal cannot carry it.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

“Changing to replace made the exception disappear”

That changes the symptom, not the contract. Compare the replaced output with the original bytes, determine the producer’s encoding, and restore strict for data that must be complete. Keep replacement only on a separately identified, lossy presentation path.

A staged migration plan for production applications

  1. Capture the complete traceback. Record the operation, codec named in the exception, filename or stream, and the first offending byte or character. Avoid editing the handler before you know the boundary.
  2. Write down each external contract. For every file, API payload, subprocess, pipe, report, and terminal, record who produces the bytes, who consumes them, and the specified encoding.
  3. Patch the narrow call sites. Add explicit encoding= arguments to built-in and path APIs, CSV/report streams, and text-mode subprocesses. Use utf-8 only where the contract says UTF-8; use locale or a named legacy codec where it does not.
  4. Enable warnings in tests. Run the suite with -X warn_default_encoding or PYTHONWARNDEFAULTENCODING=1 and make warnings failures for code you own.
  5. Test representative data. Include ASCII, accented Latin text, non-Latin scripts, emoji where supported, malformed byte sequences, empty files, and files produced by every supported upstream tool. Test both reading and writing.
  6. Test destinations. Exercise interactive terminals, redirected output, CI logs, pipes, and downstream programs. A UTF-8 input fix is incomplete if the output path still assumes ASCII.
  7. Use a temporary rollback only if required. Deploy with PYTHONUTF8=0 or -X utf8=0 while the explicit fixes are being released, then remove the rollback and repeat the test suite in UTF-8 mode.
  8. Monitor for data changes. Compare decoded records, hashes of preserved raw files, and downstream exports before and after migration. A disappearing exception can conceal mojibake or dropped characters.

Checklist for diagnosing a live incident

  • Is the exception a decode failure at a bytes-to-text boundary or an encode failure at a text-to-bytes boundary?
  • What program, service, file format, or terminal produced the bytes?
  • Does that producer explicitly document UTF-8, a locale codec, or another named encoding?
  • Does the failing Python call omit encoding= or rely on a helper’s default?
  • Is UTF-8 mode active because of Python 3.15, PYTHONUTF8=1, or -X utf8?
  • Would the selected destination actually accept every character in the string?
  • Are replace or ignore hiding data loss?
  • Can the issue be reproduced with the real producer’s bytes and the real consumer’s stream?

What not to do

  • Do not add errors='ignore' globally just to make an import complete.
  • Do not assume every legacy file should be interpreted as UTF-8 because Python 3.15 defaults to it.
  • Do not assume encoding='locale' is portable; it deliberately follows the current locale.
  • Do not convert already-mojibaked text repeatedly in an attempt to “repair” it. Preserve the original bytes and correct the first decode.
  • Do not treat PYTHONUTF8=0 as the permanent fix. It is a rollout control, not an encoding contract.

Python 3.15’s default is predictable UTF-8, but predictability does not remove the need to specify external data contracts. Once each boundary states its encoding, the same program can run across operating systems without inheriting accidental locale behavior, and genuine incompatibilities become visible at the point where they can be repaired.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

PEP 597PEP 686Python 3.15text encodingUnicodeDecodeErrorUnicodeEncodeErrorUTF-8

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Recommended PC Tool
Recommended PC Tool
PC Slower Than It Used to Be?Free scan - under a minute
Crashes, No Sound, or Screen Glitches?Free driver scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.