import yaml from 'js-yaml' returns undefined in js-yaml v5. Switch to a namespace import and you're done:
That's the whole fix. The rest of this post is why it happens, so you can spot the next dependency that does it to you.
The error you actually see
The import doesn't throw. It quietly binds yaml to undefined, and you find out at the first call site:
Confusing, because the line the stack trace points at (yaml.load(...)) is correct. Nothing changed there. What changed is the import three lines up.
Why v5 breaks it
js-yaml v5 went ESM named-only and dropped the default export. In v4 you could do import yaml from 'js-yaml' and get an object with .load and .dump on it. In v5 those are named exports only, and there is no default to bind to, so the default import resolves to undefined.
The functions are all still there. They just have to be reached by name:
If you reach for js-yaml through a dynamic import, the same removal bites there too. Drop the .default:
What it looks like in a real repo
This landed on me as a red pipeline. A Renovate MR bumped js-yaml from ^4 to v5, the diff was one line in package.json, and the unit suite went from green to eleven failures. Every failure was the same Cannot read properties of undefined at a yaml.load or yaml.dump call.
The fix touched nine files and zero logic: the two source modules that parse and write config, plus seven test files, all doing import yaml from 'js-yaml'. Swap each to import * as yaml from 'js-yaml', swap the one dynamic import's .default, and the suite went back to 1006/1006 green with no other changes. No lockfile churn, no behavior change, just the import style catching up to the package.
The pattern worth remembering
This isn't a js-yaml quirk. It's the standard shape of an ESM major bump: a package that used to ship a CommonJS-ish default export moves to named-only ESM exports, and every default import against it silently becomes undefined. You'll hit it again with other packages, and the tell is always the same, an import that used to be an object is suddenly undefined at its first method call.
Two habits make it a five-minute fix instead of a debugging session:
- Read the changelog on major bumps, not just the diff. "Removed default export" is one line in release notes and zero lines in your
package.jsondiff. - Prefer named or namespace imports for utility libraries. They survive this transition; default imports are the ones that break.
This is one case from the broader triage flow in When Renovate MRs Break the Build, the "real breaking change" bucket, the one you can't retry your way out of.