{"record":{"id":"1dd09076191262dc","repo":"langchain-ai/deepagents","slug":"skill-path-skill-path-resolves-outside-all-allow","errorCode":null,"errorMessage":"Skill path {skill_path} resolves outside all allowed skill directories. If this is a symlink, add the target directory to {EXTRA_SKILLS_DIRS} or [skills].extra_allowed_dirs in {config_file}.","messagePattern":"Skill path (.+?) resolves outside all allowed skill directories\\. If this is a symlink, add the target directory to (.+?) or \\[skills\\]\\.extra_allowed_dirs in (.+?)\\.","errorType":"exception","errorClass":"PermissionError","httpStatus":null,"severity":"error","filePath":"libs/code/deepagents_code/skills/load.py","lineNumber":215,"sourceCode":"    from pathlib import Path\n\n    path = Path(skill_path).resolve()\n\n    if allowed_roots and not any(path.is_relative_to(root) for root in allowed_roots):\n        logger.warning(\n            \"Skill path %s is outside all allowed roots, refusing to read\",\n            skill_path,\n        )\n        from deepagents_code._env_vars import EXTRA_SKILLS_DIRS\n        from deepagents_code._paths import PATHS\n\n        msg = (\n            f\"Skill path {skill_path} resolves outside all allowed skill \"\n            \"directories. If this is a symlink, add the target directory to \"\n            f\"{EXTRA_SKILLS_DIRS} or [skills].extra_allowed_dirs \"\n            f\"in {PATHS.display(PATHS.profile.config_file)}.\"\n        )\n        raise PermissionError(msg)\n\n    try:\n        return path.read_text(encoding=\"utf-8\")\n    except (OSError, UnicodeDecodeError):\n        logger.warning(\n            \"Could not read skill content from %s\", skill_path, exc_info=True\n        )\n        return None\n","sourceCodeStart":197,"sourceCodeEnd":224,"githubUrl":"https://github.com/langchain-ai/deepagents/blob/a1af029e6e73cb17c36bff823d227747b28e91e1/libs/code/deepagents_code/skills/load.py#L197-L224","documentation":"`load_skill_content` resolves the skill path and checks it against the allowlist of permitted skill roots before reading. If the resolved path escapes all allowed directories (commonly via a symlink), it raises `PermissionError` explaining how to allowlist the target directory via `EXTRA_SKILLS_DIRS` or `[skills].extra_allowed_dirs`. This is a fail-closed containment guard on skill file reads.","triggerScenarios":"`load_skill_content(skill_path)` (via `_load`, `_retry`, or `run_non_interactive`) when the resolved absolute path is outside every allowed skill root — including the symlink-escape case where the visible path is inside a root but its target is not.","commonSituations":"Skills directory symlinked to a repo elsewhere on disk; SKILL.md containing links resolved outside roots; project skills placed outside the configured project/user skills directories.","solutions":["If it is a symlink, add the target directory to the [skills].extra_allowed_dirs list in your config file or the EXTRA_SKILLS_DIRS setting","Move or copy the skill physically into an allowed skills directory","Confirm with `readlink -f <skill_path>` where the path actually resolves","Then re-run the command; trust the directory if prompted"],"exampleFix":"// before (toml)\n# [skills].extra_allowed_dirs not set; ~/my-skills symlinked into skills dir\n// after (config.toml)\n[skills]\nextra_allowed_dirs = [\"/home/me/my-skills\"]","handlingStrategy":"validation","validationCode":"from pathlib import Path\n\ndef resolves_within(skill_path: Path, allowed_roots: list[Path]) -> bool:\n    resolved = skill_path.resolve()\n    return any(root.resolve() in resolved.parents or resolved == root.resolve()\n               for root in allowed_roots)","typeGuard":"def is_contained(skill_path: Path, allowed_roots: list[Path]) -> bool:\n    resolved = skill_path.resolve()\n    return any(resolved.is_relative_to(root.resolve()) for root in allowed_roots)","tryCatchPattern":"try:\n    content = load_skill_content(skill_path)\nexcept PermissionError as exc:\n    print(f'add target dir to extra_allowed_dirs: {exc}')\n    raise SystemExit(1) from exc","preventionTips":["Run readlink -f on skill paths to see where symlinks actually point","Register external skill locations in [skills].extra_allowed_dirs instead of symlinking","Keep skills inside the configured project/user skills directories","Review SKILL.md targets for paths outside the roots"],"tags":["security","symlink","path-validation"],"backgroundTag":"path-outside-allowed-roots","analyzedSha":"a1af029e6e73cb17c36bff823d227747b28e91e1","analyzedAt":"2026-08-29T11:43:24.718Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}