Diagnosing ReadTheDocs Build Failures from .readthedocs.yaml Misconfiguration
ReadTheDocs build failing with YAML errors, ModuleNotFoundError, or missing artifacts? A diagnostic table, ordered checks, and a pinned reference config to isolate the cause fast.
02 Jun 2026, 06:29 UTC

The recognizable condition
Your ReadTheDocs build fails before rendering a single page, and the raw build log shows one of a small set of messages: Configuration error: Invalid YAML, ModuleNotFoundError during the Sphinx step, Build exceeded maximum allowed time, or No version selected for build. In nearly every case the root cause sits in .readthedocs.yaml or in the project's version settings — not in your documentation source. This guide walks through the checks in the order that isolates the cause fastest.
Assumption: this applies to readthedocs.org builds using config file v2, the current schema. If your project has no .readthedocs.yaml at all, ReadTheDocs falls back to legacy defaults, which is itself a common source of drift — adding an explicit config file is the first fix.
Cause and diagnostic table
| Symptom in build log | Likely cause | Where to look |
|---|---|---|
| Configuration error at the very first step | YAML syntax or indentation error | .readthedocs.yaml formatting |
| Missing system tools or native libs (libjpeg, libffi) | Wrong or unpinned build.image | build.image key |
| ModuleNotFoundError for your package or Sphinx extension | python.version mismatch or missing install step | python.version, python.install |
| pip install fails: file not found | requirements path wrong relative to repo root | python.install requirements entry |
| Build succeeds but no htmlzip/pdf artifact | formats list missing the output | formats key |
| No version selected for build | Version not Active in dashboard | Admin → Versions |
| Exit 137 or timeout near 15 minutes | Platform resource limit, not config | Escalate (see below) |
Ordered checks
- Validate the YAML locally. Install the CLI with
pip install readthedocs-cli, then runreadthedocs lint .readthedocs.yamlfrom your repo root. This catches syntax and schema errors before you burn a build. Note it does not catch runtime environment problems — a lint-clean file can still fail at the Sphinx step. - Check build.image. If the key is absent or set to a moving alias, pin it:
build: os: ubuntu-22.04. The unpinned default changes without notice, and ReadTheDocs does not install apt packages for you — if an extension needs a system library, it must already exist in the chosen image. - Match python.version to your tested version. Set
tools: python: "3.11"(or whatever your CI tests against). Compare this against the interpreter version printed in the build log's environment-setup phase. A mismatch here is the classic source of ModuleNotFoundError when a dependency dropped support for the older interpreter. - Verify the requirements path. Paths in
python.installare relative to the repo root, not the docs folder. If your file lives atdocs/requirements.txt, the entry must say exactly that. - Check formats. If you expect downloadable artifacts, declare them:
formats: [htmlzip, pdf, epub]. HTML is always built; the rest are opt-in. - Confirm version activation. In the dashboard under Admin → Versions, the target version must be both Active and Built to be served publicly. A webhook-triggered build can complete while the version stays Inactive.
A working reference configuration
This example pins everything that otherwise drifts. Adjust the Python version and paths to your project:
version: 2
build:
os: ubuntu-22.04
tools:
python: "3.11"
python:
install:
- requirements: docs/requirements.txt
formats:
- htmlzip
- pdf
The engineering decision worth making deliberately: keep a dedicated docs/requirements.txt with pinned Sphinx and theme versions (e.g., sphinx==7.2.*). Pulling unpinned docs dependencies means an upstream release can break your build with zero changes on your side. Similarly, enable automatic tag builds only for semver tags if you want to control version proliferation in the dashboard.
Fixes tied to findings
- YAML error → fix indentation, quote version strings like
"3.11"(unquoted3.10parses as the float 3.1), re-run lint. - Missing system packages → pin
os: ubuntu-22.04; if the library still is not in the image, replace the extension or vendor a pure-Python alternative — you cannot apt-install. - ModuleNotFoundError → align
tools.pythonwith your requirements, and add apython.installentry for your package itself if Sphinx imports it (e.g.,method: pip, path: .) for autodoc. - Missing artifacts → add the format to the list and rebuild; verify in the build's artifact download section.
- Version not building → activate it in Admin → Versions, or configure automation rules for branches/tags.
Verifying the fix
Trigger a fresh build from the dashboard (Build a version) rather than assuming a push will do it. In the raw log, confirm: the config parses at step one, the interpreter version matches your YAML, pip installs your requirements file without a path error, and Sphinx completes. Then check that the expected artifacts appear and that Admin → Versions shows Active + Built.
Escalation criteria
If lint passes, the config matches the reference above, and the build still fails with exit code 137 (out of memory) or consistently hits the 15-minute timeout on ubuntu-22.04, the problem is platform resource limits, not your configuration. First try reducing Sphinx workload — exclude generated content, limit autodoc scope, or split large API docs. If that is not viable, open a ReadTheDocs support ticket including the build URL, your .readthedocs.yaml, and docs/requirements.txt. Note that exact image contents and resource limits change over time, so verify current documented limits before assuming a regression.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.