{"record":{"id":"74e113adb37c633b","repo":"langchain-ai/deepagents","slug":"cannot-determine-whether-str-left-r-is-str-rig","errorCode":null,"errorMessage":"Cannot determine whether {str(left)!r} is {str(right)!r}: {exc.strerror or exc}. Fix the permissions on those paths, or set DEEPAGENTS_HOME to a path that can be read.","messagePattern":"Cannot determine whether (.+?) is (.+?): (.+?)\\. Fix the permissions on those paths, or set DEEPAGENTS_HOME to a path that can be read\\.","errorType":"exception","errorClass":"DeepAgentsHomeError","httpStatus":null,"severity":"error","filePath":"libs/code/deepagents_code/_paths.py","lineNumber":662,"sourceCode":"    try:\n        return Path(left).samefile(right)\n    except FileNotFoundError:\n        # A profile root that is not there yet cannot be the home directory,\n        # and the lexical comparison above has already ruled out the\n        # spelling-only case.\n        logger.debug(\"Could not compare %s with %s: one is missing\", left, right)\n        return False\n    except OSError as exc:\n        # EACCES, ELOOP, EIO, ESTALE: the answer is unknown, not \"different\".\n        # The whole point of `samefile` here is to catch the non-lexical\n        # spellings the comparison above misses, so returning `False` would\n        # accept exactly the aliases this guard exists to reject.\n        msg = (\n            f\"Cannot determine whether {str(left)!r} is {str(right)!r}: \"\n            f\"{exc.strerror or exc}. Fix the permissions on those paths, or \"\n            \"set DEEPAGENTS_HOME to a path that can be read.\"\n        )\n        raise DeepAgentsHomeError(msg) from exc\n\n\ndef _reject_degenerate_root(root: Path, launch_home: Path | None) -> None:\n    \"\"\"Reject a resolved profile root that would scatter state.\n\n    A profile root is a trust boundary that owns everything beneath it, so it\n    must be a directory of its own. The rejected cases all resolve to something\n    the user did not mean:\n\n    - The filesystem root, from `DEEPAGENTS_HOME=/` or a `..` chain that walks\n      past it, would put credentials in `/.state/auth.json`.\n    - The home directory itself, from a `DEEPAGENTS_HOME=~/` typo, would make\n      the profile dotenv the user's generic `~/.env` and load it as trusted\n      configuration.\n    - An existing non-directory cannot hold a profile at all.\n    - A root that exists but cannot be read. Every later access fails one file\n      at a time, and each failure looks like a first run, so reject it once\n      here with the real cause.","sourceCodeStart":644,"sourceCodeEnd":680,"githubUrl":"https://github.com/langchain-ai/deepagents/blob/a1af029e6e73cb17c36bff823d227747b28e91e1/libs/code/deepagents_code/_paths.py#L644-L680","documentation":"_same_directory compares two paths for identity (to reject degenerate profile roots like DEEPAGENTS_HOME=/ or /tmp). If the underlying OS calls raise OSError (e.g. EACCES while stat'ing the paths), it re-raises as DeepAgentsHomeError because the library cannot prove the paths differ. This is a fail-closed guard on the trust boundary.","triggerScenarios":"DEEPAGENTS_HOME points at a path whose permission bits prevent stat/read of it or the comparison target (e.g. root or a parent is unreadable), during _reject_degenerate_root checks.","commonSituations":"DEEPAGENTS_HOME under a root-owned directory with mode 700 while running as a normal user; restricted mount points in containers; NFS/ACL setups where stat is denied.","solutions":["Fix permissions on the path and its parents (chmod/chown so the current user can read+search them).","Point DEEPAGENTS_HOME at a readable, user-owned absolute path.","Avoid placing the profile root under system-restricted directories like / or /root."],"exampleFix":"// before: DEEPAGENTS_HOME=/root/.deepagents as non-root user\n// after\nexport DEEPAGENTS_HOME=\"$HOME/.deepagents\"","handlingStrategy":"try-catch","validationCode":"import os\nfrom pathlib import Path\nroot = Path(os.environ.get('DEEPAGENTS_HOME', ''))\nif root.exists():\n    for p in (root, *root.parents[:2]):\n        if not os.access(p, os.R_OK | os.X_OK):\n            raise SystemExit(f'unreadable path in DEEPAGENTS_HOME chain: {p}')","typeGuard":null,"tryCatchPattern":"try:\n    root = _resolve_profile_root()\nexcept DeepAgentsHomeError as exc:\n    if 'Cannot determine whether' in str(exc):\n        raise SystemExit(f'Fix permissions for DEEPAGENTS_HOME: {exc}') from exc\n    raise","preventionTips":["Keep DEEPAGENTS_HOME under the user's own home directory.","Avoid pointing the profile root at /, /tmp, /root, or admin-only trees.","Verify readability of the full path chain after restores or volume mounts."],"tags":["permissions","filesystem","configuration","security"],"backgroundTag":"permission-denied","analyzedSha":"a1af029e6e73cb17c36bff823d227747b28e91e1","analyzedAt":"2026-08-29T11:43:24.718Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}