langchain-ai/deepagents · error · OSError
debug log directory is not a real directory: {path}
Error message
debug log directory is not a real directory: {path} What it means
_prepare_debug_directory creates the per-thread debug log directory with mode 0o700 and then verifies it is a genuine directory and not a symlink (Windows check via lstat). If lstat reveals the path is a symlink or not a directory, the library refuses to write debug logs there to prevent symlink attacks on debug output. This is a security hardening error, not a normal usage failure.
Source
Thrown at libs/code/deepagents_code/_debug.py:302
valid = ", ".join(LOG_LEVELS)
message = f"ignoring invalid {LOG_LEVEL}={raw!r}; expected one of {valid}"
_warn(message)
return fallback
def _prepare_debug_directory(path: Path) -> None:
"""Create or tighten the debug directory to owner-only access.
Raises:
OSError: If the directory cannot be created, opened, or tightened.
"""
with contextlib.suppress(FileExistsError):
path.mkdir(mode=0o700)
if os.name == "nt":
metadata = path.lstat()
if stat.S_ISLNK(metadata.st_mode) or not stat.S_ISDIR(metadata.st_mode):
msg = f"debug log directory is not a real directory: {path}"
raise OSError(msg)
_set_windows_owner_only_dacl(path)
return
flags = os.O_RDONLY | getattr(os, "O_DIRECTORY", 0) | getattr(os, "O_NOFOLLOW", 0)
fd = os.open(path, flags)
try:
metadata = os.fstat(fd)
if metadata.st_uid != os.geteuid():
msg = f"debug log directory is not owned by the current user: {path}"
raise OSError(msg)
os.fchmod(fd, 0o700)
finally:
os.close(fd)
def _thread_log_name(thread_id: str) -> str:
"""Return a traversal-safe log filename for a thread identifier."""
if (
len(thread_id) <= _MAX_THREAD_FILENAME_LENGTHView on GitHub (pinned to a1af029e6e)
Solutions
- Delete the path at the reported location (it is a symlink or a file, not a real directory) and let the library recreate it.
- Ensure only trusted processes can write to the parent directory where the debug log dir is created.
- Retry after removing the bad entry; the next bind_debug_logging_to_thread call will recreate it with mode 0o700.
Example fix
// before (path is a symlink) debug/logs -> /somewhere/else // after (remove and recreate) rm debug/logs mkdir debug/logs
Defensive patterns
Strategy: validation
Validate before calling
import os, stat
p = Path(debug_dir)
if p.is_symlink() or (p.exists() and not p.is_dir()):
p.unlink() # remove bad entry before binding debug logging Type guard
def is_real_directory(p: Path) -> bool:
try:
return not p.is_symlink() and stat.S_ISDIR(p.lstat().st_mode)
except OSError:
return False Try / catch
try:
bind_debug_logging_to_thread(thread_id)
except OSError as exc:
if 'not a real directory' in str(exc):
shutil.rmtree(path, ignore_errors=True) or os.remove(path)
bind_debug_logging_to_thread(thread_id) # retry once Prevention
- Keep the debug log parent directory writable only by trusted processes/users.
- Clean stale symlinks from temp/cache directories before startup.
- On Windows, audit for junctions/symlinks at known dcode paths.
When it happens
Trigger: On Windows only: an attacker (or leftover artifact) replaced the debug log path with a symlink or a non-directory file between mkdir and the lstat check, and bind_debug_logging_to_thread was called.
Common situations: A stale symlink left at the debug directory location from a previous setup; a temp-cleanup tool replaced the directory with a junction; another process races to substitute a symlink at the path.
Related errors
- debug log directory is not owned by the current user: {path}
- Cannot determine whether {str(left)!r} is {str(right)!r}: {e
- temporary artifact is not a regular file
- temporary artifact is not owned by this user
- temporary artifact permissions are too broad
AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29).
Data as JSON: /api/errors/b2e7f32206fb5e43.
Report an issue: GitHub.