Skip to content

Fix: Sphinx Not Working — Autodoc Import Errors, Theme Issues, and Cross-References

FixDevs ·

Quick Answer

How to fix Sphinx errors — autodoc cannot import module, theme not found, intersphinx links broken, ReadTheDocs build failed, MyST markdown not rendering, and unknown directive in conf.py.

The Error

You run sphinx-build and autodoc can’t import your module:

WARNING: autodoc: failed to import module 'mypackage'; the following exception was raised:
No module named 'mypackage'

Or the theme you configured silently falls back:

# conf.py
html_theme = "furo"
$ make html
# Builds with the default "alabaster" theme instead — no error

Or cross-references to external docs (:py:class:\numpy.ndarray“) don’t link:

Unknown interpreted text role "py:class".
WARNING: undefined label: 'numpy.ndarray'

Or ReadTheDocs builds locally but fails on the platform:

Could not import extension myst_parser (exception: No module named 'myst_parser')
ERROR: Service unavailable

Or MyST markdown files render as plain text:

# index.md (in a Sphinx project)
Hello *world*

Output: Hello *world* instead of italicized “world”.

Sphinx is the universal documentation tool for Python — RST or Markdown source, beautiful HTML output, cross-referenced API docs from docstrings, hosted on ReadTheDocs. But the configuration is intricate: extensions need to be both installed and listed in conf.py, themes need proper imports, MyST parser needs explicit setup, and the build environment must match exactly between local and CI. This guide covers each common failure.

Why This Happens

Sphinx is a Python program — autodoc imports your modules to extract docstrings. If your package isn’t on the Python path (or has import errors), autodoc silently skips it with a warning. The “build succeeded with warnings” message hides serious problems.

ReadTheDocs runs sphinx-build in an isolated environment that doesn’t include your dev dependencies by default. You must explicitly tell RTD what to install — usually via a requirements.txt for docs or a section in .readthedocs.yaml.

Fix 1: Project Setup and conf.py

# Initialize a Sphinx project
mkdir docs && cd docs
sphinx-quickstart

# Or use the full directory layout
sphinx-quickstart --sep --quiet \
    --project="MyProject" \
    --author="Your Name" \
    --release="1.0.0" \
    --language="en"

Resulting structure:

docs/
├── source/
│   ├── conf.py
│   ├── index.rst
│   └── _static/
├── build/   (gitignored)
└── Makefile

Build:

cd docs
make html        # On macOS/Linux
make.bat html    # On Windows

# Output: build/html/index.html

Minimal conf.py:

# docs/source/conf.py
import os
import sys

# Add project root to Python path so autodoc can import it
sys.path.insert(0, os.path.abspath("../.."))

project = "MyProject"
author = "Your Name"
release = "1.0.0"

extensions = [
    "sphinx.ext.autodoc",        # Generate API docs from docstrings
    "sphinx.ext.napoleon",        # Support Google/NumPy docstring styles
    "sphinx.ext.intersphinx",     # Cross-reference other projects' docs
    "sphinx.ext.viewcode",        # Add "view source" links
    "myst_parser",                 # Markdown support
]

# Source file types
source_suffix = {
    ".rst": "restructuredtext",
    ".md": "markdown",
}

# Theme
html_theme = "furo"   # or "sphinx_rtd_theme", "pydata_sphinx_theme", etc.
html_static_path = ["_static"]

# Cross-references to other projects
intersphinx_mapping = {
    "python": ("https://docs.python.org/3", None),
    "numpy": ("https://numpy.org/doc/stable", None),
    "pandas": ("https://pandas.pydata.org/docs", None),
}

Common Mistake: Forgetting sys.path.insert(0, ...) for autodoc. Sphinx runs from docs/source/; your package is at the project root. Without the path insert, autodoc can’t import your modules and silently produces empty documentation. The warning is in the build log but easy to miss.

Fix 2: Autodoc — Generating API Docs From Docstrings

.. autoclass:: mypackage.MyClass
   :members:
   :undoc-members:
   :show-inheritance:

.. automodule:: mypackage.utils
   :members:

Common autodoc options:

OptionBehavior
:members:Document public members
:undoc-members:Include members without docstrings
:private-members:Include _name members
:special-members: __init__Include specific dunders
:exclude-members: foo, barHide specific members
:show-inheritance:Show base classes
:inherited-members:Include members from parent classes

Auto-summary (cleaner API docs):

# conf.py
extensions = [
    "sphinx.ext.autodoc",
    "sphinx.ext.autosummary",
]
autosummary_generate = True   # Auto-generate stub files
.. autosummary::
   :toctree: api/
   :recursive:

   mypackage

This generates separate page per module/class — much cleaner than one giant API page.

Napoleon for Google/NumPy docstring formats:

def fetch(url: str, timeout: float = 30.0) -> dict:
    """Fetch a URL and return JSON.

    Args:
        url: The URL to fetch.
        timeout: Request timeout in seconds.

    Returns:
        Parsed JSON response.

    Raises:
        ConnectionError: If the host is unreachable.
        ValueError: If the response isn't valid JSON.

    Example:
        >>> data = fetch("https://api.example.com/users")
        >>> data["count"]
        42
    """

Without Napoleon, that docstring renders as plain text. With Napoleon, it becomes a beautifully formatted API doc with separated Arguments/Returns/Raises sections.

Fix 3: Markdown Support with MyST

Sphinx defaults to reStructuredText. For Markdown:

pip install myst-parser
# conf.py
extensions = [
    "myst_parser",
]
source_suffix = {
    ".rst": "restructuredtext",
    ".md": "markdown",
}

Now .md files work:

# My Page

Some content with **bold** and *italic*.

Links: [Click me](other.md)

Code:

```python
def hello():
    return "world"

**MyST adds extensions** for Sphinx-specific features in Markdown:

```python
# conf.py
myst_enable_extensions = [
    "colon_fence",       # ::: for directives
    "deflist",           # Definition lists
    "fieldlist",         # :field: lists
    "linkify",            # Auto-link URLs
    "substitution",       # ${variable} substitution
    "tasklist",           # GitHub-style [ ] [x] task lists
]

Directives in MyST (colon fence syntax):

:::{note}
This is a note callout.
:::

:::{warning}
This is a warning.
:::

:::{admonition} Custom Title
:class: tip
Content with a custom title.
:::

Cross-references in MyST:

See {ref}`my-section` or {py:func}`mypackage.utils.fetch`.

(my-section)=
## My Section

This section is referenceable.

For a Markdown alternative when you don’t need Sphinx’s full power, see MDX not working for MDX patterns in Astro/Next.js docs sites.

Fix 4: Themes

# conf.py
html_theme = "furo"

Install the theme first:

# Furo — modern, clean (highly recommended)
pip install furo

# Read the Docs (classic, used by many projects)
pip install sphinx-rtd-theme

# PyData (used by NumPy, Pandas, scikit-learn)
pip install pydata-sphinx-theme

# Book theme (Jupyter Book style)
pip install sphinx-book-theme

Without the install, Sphinx silently falls back to alabaster (built-in default) and emits a warning. The “theme not found” warning is the tell — fix it by installing.

Pro Tip: Furo is the modern default for new Sphinx projects — clean design, great mobile support, dark/light mode toggle, syntax highlighting that works out of the box. Use sphinx-rtd-theme only if you specifically want the ReadTheDocs aesthetic or are matching an existing project.

Theme configuration:

# conf.py — Furo
html_theme = "furo"
html_title = "MyProject Docs"
html_logo = "_static/logo.png"
html_favicon = "_static/favicon.ico"
html_theme_options = {
    "source_repository": "https://github.com/me/myproject",
    "source_branch": "main",
    "source_directory": "docs/source/",
    "light_logo": "logo-light.png",
    "dark_logo": "logo-dark.png",
}

html_static_path for custom CSS:

html_static_path = ["_static"]
html_css_files = ["custom.css"]
/* docs/source/_static/custom.css */
:root {
    --color-brand-primary: #0066CC;
}

Fix 5: Cross-References (intersphinx)

Link to other projects’ docs without hardcoding URLs:

# conf.py
intersphinx_mapping = {
    "python": ("https://docs.python.org/3", None),
    "numpy": ("https://numpy.org/doc/stable", None),
    "pandas": ("https://pandas.pydata.org/docs", None),
    "django": ("https://docs.djangoproject.com/en/stable", "https://docs.djangoproject.com/en/stable/_objects/"),
}

Reference syntax (RST):

The :py:class:`numpy.ndarray` is the core array type.
See :py:func:`numpy.array` for construction.
The :py:meth:`pandas.DataFrame.groupby` method ...

MyST equivalent:

The {py:class}`numpy.ndarray` is the core array type.
See {py:func}`numpy.array` for construction.

Common roles:

RoleTarget
:py:class:Python class
:py:func:Python function
:py:meth:Python method
:py:mod:Python module
:py:attr:Python attribute
:py:exc:Python exception
:py:obj:Generic Python object
:ref:Section ref in your own docs
:doc:Other doc page

Common Mistake: Writing :class:\numpy.ndarray`(withoutpy:prefix). Without the explicit domain, Sphinx defaults tostd, which doesn't know about Python classes. Always use:py:class:` for Python references.

Check what intersphinx knows:

python -m sphinx.ext.intersphinx https://docs.python.org/3/objects.inv
# Prints every available reference

Fix 6: Building and Iterating

cd docs

# Standard build
make html

# Watch mode — rebuild on changes (requires sphinx-autobuild)
pip install sphinx-autobuild
sphinx-autobuild source build/html --open-browser

# Clean rebuild (force full rebuild)
make clean && make html

# Build with warnings as errors (for CI)
sphinx-build -W -b html source build/html

sphinx-autobuild is essential for iterative writing — it serves the docs on localhost, watches for changes, and auto-refreshes the browser.

Build with parallelism:

sphinx-build -j auto -b html source build/html
# -j auto uses all CPU cores

Multiple output formats:

make html       # HTML (default)
make epub       # EPUB e-book
make latex      # LaTeX (PDF prep)
make latexpdf   # Builds LaTeX then runs pdflatex
make man        # Unix man pages
make linkcheck  # Validate all external links

make linkcheck is invaluable in CI — catches broken external links before users do.

Fix 7: ReadTheDocs Configuration

ReadTheDocs runs Sphinx on every push. Configuration goes in .readthedocs.yaml:

# .readthedocs.yaml (at repo root)
version: 2

build:
  os: ubuntu-22.04
  tools:
    python: "3.12"

python:
  install:
    - requirements: docs/requirements.txt
    - method: pip
      path: .
      extra_requirements:
        - docs

sphinx:
  configuration: docs/source/conf.py
  fail_on_warning: false   # Set to true to enforce warning-free builds

docs/requirements.txt lists doc dependencies:

sphinx>=7
furo>=2024.1
myst-parser>=2
sphinx-autobuild>=2024.1

Or via pyproject.toml extras:

[project.optional-dependencies]
docs = [
    "sphinx>=7",
    "furo>=2024.1",
    "myst-parser>=2",
]
# .readthedocs.yaml
python:
  install:
    - method: pip
      path: .
      extra_requirements: [docs]

Common Mistake: Building locally with pip install -e . (editable), then on RTD only installing doc deps. Autodoc can’t find your package because it’s not actually installed. Either install the package in .readthedocs.yaml (via method: pip with path: .) or add sys.path.insert(0, ...) in conf.py.

Test the RTD build locally:

# Mirror RTD's build environment
docker run --rm -it -v $PWD:/docs python:3.12 bash
cd /docs
pip install -r docs/requirements.txt
pip install .
sphinx-build -b html docs/source docs/build

Fix 8: Common Build Errors

ERROR: Could not import extension myst_parser

The extension isn’t installed in the build environment. Add to your docs requirements.

WARNING: autodoc: failed to import module 'mypackage'

Either:

  • Module not on Python path (add sys.path.insert(0, ...) in conf.py)
  • Module has import errors (run python -c "import mypackage" to verify)
  • C extensions can’t be built in the docs environment (use autodoc_mock_imports)

Mock C extensions and heavy dependencies:

# conf.py
autodoc_mock_imports = [
    "torch",
    "tensorflow",
    "cv2",   # OpenCV (large)
    "MyProject._extension",   # Internal C extension
]

Sphinx replaces these imports with mocks — autodoc generates docs without actually loading them. Useful when those deps don’t make sense in the docs environment.

WARNING: undefined label: 'my-section'

You used :ref:\my-section“ but didn’t define the label. Add a label above the section:

.. _my-section:

My Section
----------
WARNING: 'page.rst' isn't included in any toctree

The page exists but isn’t reachable from the top-level index.rst. Add it to a toctree:

.. toctree::
   :maxdepth: 2

   page
   other-page

Or suppress the warning for orphan pages:

:orphan:

My Standalone Page
==================

Still Not Working?

Sphinx vs MkDocs

  • Sphinx — Mature, RST-first (but supports MyST markdown), complex API doc features, intersphinx, used by every major Python project.
  • MkDocs — Markdown-first, simpler, faster builds. Best for prose-heavy docs without complex API references.

Use Sphinx when you need autodoc and cross-referenced API docs. Use MkDocs (with the Material theme) for tutorial-heavy or marketing-flavored docs.

Doctest in Documentation

Sphinx can run code blocks in your docstrings as tests:

def add(a: int, b: int) -> int:
    """Add two integers.

    Example:
        >>> add(2, 3)
        5
        >>> add(-1, 1)
        0
    """
    return a + b
make doctest
# Runs all >>> examples and checks output matches

Brilliant for keeping examples accurate — broken examples fail the build.

Version Switcher

PyData and Furo themes support a version switcher in the navbar. Configure in conf.py:

html_theme_options = {
    "switcher": {
        "json_url": "https://myproject.io/_static/switcher.json",
        "version_match": release,
    },
}

The JSON lists versions with URLs. ReadTheDocs handles this automatically via its “Activate version” UI.

Integration with pytest

For pytest fixture patterns that exercise documented code examples, see pytest fixture not found. For pre-commit hooks that run make linkcheck or sphinx-build -W in CI, see pre-commit not working.

Type Hints in Docs

The sphinx_autodoc_typehints extension shows type annotations alongside docstrings:

pip install sphinx-autodoc-typehints
extensions = [
    "sphinx.ext.autodoc",
    "sphinx_autodoc_typehints",   # Must come AFTER autodoc
]

For mypy-based type checking that pairs with documented type hints, see Python mypy type error.

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles