sphinx.ext.viewcode generates a module page with no source links
Describe the bug
Hej
while investigating options for a parallel build of my project, I noticed that a parallel build did not produce the same output as a non-parallel build.
Looking closer, it seems that sphinx.ext.viewcode can generate a module page even though no documented object links to it.
A clean parallel HTML build creates:
_modules/index.html
_modules/pkg/_types.htmlThe documented object has no [source] link, and the module page has no [docs] backlink. The page only contains the highlighted source of the whole module. When I run the same clean build without parallel workers, neither file is created.
PEP 695 is not required to reproduce the problem. I have seen the same result with other kinds of objects, but this example uses a PEP 695 type alias because that is how I originally found the issue.
How to Reproduce
I was able to reproduced this with the following minimal project. It requires Python 3.12 or newer because it uses the PEP 695 type statement.
One can create the minimalproject with these commands:
mkdir -p viewcode-parallel-repro/docs viewcode-parallel-repro/pkg
cd viewcode-parallel-repro
cat > pkg/__init__.py <<'EOF'
from pkg._types import Mode
__all__ = ['Mode']
EOF
cat > pkg/_types.py <<'EOF'
from typing import Literal
type Mode = Literal['a', 'b']
EOF
cat > docs/conf.py <<'EOF'
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parents[1]))
project = 'viewcode-parallel-repro'
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode']
EOF
cat > docs/api.rst <<'EOF'
API
===
.. autodata:: pkg.Mode
EOF
cat > docs/index.rst <<'EOF'
Reproduction
============
.. toctree::
api
filler-1
filler-2
filler-3
filler-4
filler-5
EOF
printf 'Filler 1\n========\n' > docs/filler-1.rst
printf 'Filler 2\n========\n' > docs/filler-2.rst
printf 'Filler 3\n========\n' > docs/filler-3.rst
printf 'Filler 4\n========\n' > docs/filler-4.rst
printf 'Filler 5\n========\n' > docs/filler-5.rstThe five filler documents make the project large enough for Sphinx to use parallel readers.
Then one can run the fresh non-parallel and parallel builds into separate directories:
sphinx-build -E -a -W -j 1 -b html docs build/serial
sphinx-build -E -a -W -j 2 -b html docs build/parallelBoth builds succeed. The grouped source ranges in the parallel log confirmed that worker reading was used:
Then to check the generated module files:
find build/serial -path '*/_modules/*' -type f
find build/parallel -path '*/_modules/*' -type fThe first command returns nothing. The parallel build returns:
build/parallel/_modules/index.html
build/parallel/_modules/pkg/_types.htmlFinally, to check for source link, source block, and backlink:
grep -nE 'viewcode-link|\[source\]' \
build/serial/api.html build/parallel/api.html
grep -nE 'viewcode-block|viewcode-back' \
build/parallel/_modules/pkg/_types.htmlNeither command returns a match.
Environment Information
Platform: linux (Linux-7.0.0-31-generic-x86_64-with-glibc2.43)
Python version: 3.13.13 (main, Apr 7 2026, 20:49:46) [Clang 22.1.1 ])
Python implementation: CPython
Sphinx version: 9.1.0
Docutils version: 0.22.4
Jinja2 version: 3.1.6
Pygments version: 2.21.0Sphinx extensions
['sphinx.ext.autodoc', 'sphinx.ext.viewcode']Additional context
This looks similar to #8756, but that issue concerned stale output after an incremental build. This example uses fresh builds with -E -a.
I can also reproduce the problem with objects other than a PEP 695 type alias. PEP 695 is only used here because that is how I originally found the issue.
Source: sphinx-doc/sphinx