pathlib Path.walk() Puts Symlinked Directories in files, Not dirs (tested)
Python 3.12 added Path.walk() as a pathlib-native alternative to os.walk(). We built a tree with a symlink pointing at a directory and walked it with both, at their default settings, and got different classifications for the exact same entry.
1. Same tree, different dirs/files split
from pathlib import Path
import os
base = Path("/tmp/walktest")
# sub/link_to_dir -> ../linked_target (a real directory)
for root, dirs, files in base.walk():
if root.name == "sub":
print("Path.walk():", "dirs:", dirs, "files:", files)
for root, dirs, files in os.walk(base):
if os.path.basename(root) == "sub":
print("os.walk(): ", "dirs:", dirs, "files:", files)
Path.walk(): dirs: [] files: ['link_to_dir', 'file.txt']
os.walk(): dirs: ['link_to_dir'] files: ['file.txt']
Measured: both default to not following symlinks for recursion (os.walk(followlinks=False), Path.walk(follow_symlinks=False)) -- but they disagree on which list a symlinked directory belongs in. os.walk() still recognises it as a directory and puts it in dirs, just without descending into it. Path.walk() puts the identical entry in files instead.
2. The entry really is a directory
link = Path("/tmp/walktest/sub/link_to_dir")
print("is_dir():", link.is_dir())
print("is_symlink():", link.is_symlink())
is_dir(): True
is_symlink(): True
Measured: is_dir() follows the symlink and confirms the target is a real directory. Path.walk()'s default classification contradicts that -- code that does for f in files: open(root/f) will try to open() a directory and get an IsADirectoryError, while code written for os.walk()'s convention (symlinked dirs appear in dirs, just not recursed into) silently loses that entry when ported to Path.walk().
3. Fix: pass follow_symlinks=True to match os.walk()'s classification
for root, dirs, files in base.walk(follow_symlinks=True):
if root.name == "sub":
print("dirs:", dirs, "files:", files)
dirs: ['link_to_dir'] files: ['file.txt']
Measured: with follow_symlinks=True, Path.walk() classifies the symlinked directory the same way os.walk() does by default, and will also recurse into it. There is no option that gives "classify as a directory, but don't recurse" the way os.walk(followlinks=False) does -- it's classification and recursion tied to the same flag in Path.walk().
Frequently asked questions
Does Path.walk() behave like os.walk() by default?
Not for symlinked directories -- we measured Path.walk() putting a symlink-to-directory in the files list by default, while os.walk() puts the same entry in dirs even with its matching followlinks=False default.
Why would a symlinked directory end up in files under Path.walk()?
Path.walk()'s default follow_symlinks=False affects both whether it recurses into the symlink and which list it's classified under, unlike os.walk() which only ties followlinks to recursion, not classification.
How do I make Path.walk() match os.walk()'s dirs/files split?
We confirmed passing follow_symlinks=True to Path.walk() classifies symlinked directories into dirs like os.walk() does -- but it also makes Path.walk() recurse into them, which os.walk()'s default does not.
What happens if code assumes files only contains regular files?
We measured open() on a files-list entry that was actually a symlinked directory raising IsADirectoryError -- code ported from os.walk() to Path.walk() without accounting for this will crash on trees containing symlinked directories.
Is this difference documented?
It follows from each function's own parameter semantics (follow_symlinks vs followlinks controlling different things), but we did not find it called out as a migration gotcha -- we measured the discrepancy directly rather than relying on a doc note.
Written with AI assistance. Measured on 2026-10-04 with Python 3.13.5's built-in pathlib and os modules: built a directory containing a symlink that points at another directory, then compared Path.walk() (added in 3.12) against the classic os.walk() on the identical tree with both at default settings. PlainHeaders is our extension; Python's pathlib and os modules are built in.