hermes - ✅(Solved) Fix hermes profile --clone-all crashes with RecursionError on recursive symlink [2 pull requests, 1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
NousResearch/hermes-agent#11560Fetched 2026-04-18 06:00:15
View on GitHub
Comments
1
Participants
2
Timeline
6
Reactions
0
Author
Participants
Timeline (top)
referenced ×3cross-referenced ×2commented ×1

Error Message

Traceback

Root Cause

shutil.copytree is called without symlinks=True, so it follows symlinks rather than copying them as symlinks. If any symlink in ~/.hermes points to a parent directory, this causes infinite recursion.

Fix Action

Fix

Pass symlinks=True to the shutil.copytree call in the profile clone code.

Workaround

Use export/import instead: hermes profile export default ./backup.tar.gz hermes profile import ./backup.tar.gz newprofilename

PR fix notes

PR #11573: fix(profiles): preserve symlinks when cloning and exporting profiles

Description (problem / solution / changelog)

What does this PR do?

shutil.copytree is called in three places in hermes_cli/profiles.py without symlinks=True, so it follows symlinks instead of copying them as-is. A symlink inside ~/.hermes that points at a parent directory (easy to create by accident - @Opfour hit one while cloning profiles) sends shutil._copytree into infinite recursion and hermes profile create --clone-all crashes with RecursionError. The export paths would crash the same way if a user ever triggered them on such a profile.

Pass symlinks=True to all three call sites. symlinks=True copies symlinks verbatim rather than walking their target, which is the right semantic when cloning or exporting a user-owned profile tree. The existing ignore= callbacks at the export sites are preserved.

Related Issue

Fixes #11560

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

  • hermes_cli/profiles.py:437 - add symlinks=True to the --clone-all shutil.copytree call (the crash site named in the issue).
  • hermes_cli/profiles.py:802 - add symlinks=True to the default-profile export path. Same latent bug.
  • hermes_cli/profiles.py:815 - add symlinks=True to the named-profile export path. Same latent bug. Existing ignore= callbacks (credential files, _default_export_ignore) are untouched and still run.
  • tests/hermes_cli/test_profiles.py - add test_clone_all_preserves_recursive_symlink using the existing profile_env fixture. The test creates a source profile with a loop -> .. symlink, calls create_profile(clone_all=True), and asserts the call returns, the destination exists, and the symlink survives as a symlink.

How to Test

  1. Reproduce (pre-fix): mkdir -p /tmp/h/profiles/src && ln -s .. /tmp/h/profiles/src/loop && HERMES_HOME=/tmp/h hermes profile create dup --clone-all --clone-from src --no-alias -> RecursionError: maximum recursion depth exceeded from shutil._copytree.
  2. Post-fix: same commands succeed. The new dup/ profile contains the loop entry as a symlink (ls -la shows loop -> ..).
  3. Targeted test: pytest tests/hermes_cli/test_profiles.py -v - 88 passed including the new regression test.

Demo: https://vhs.charm.sh/vhs-2QQWVciEMvWZk9M4DRyHnF.gif

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/hermes_cli/test_profiles.py -v and all tests pass (88 passed)
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: macOS 15.3

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — or N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — or N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guidesymlinks=True works identically on Linux/macOS; on Windows, Python's stdlib shutil.copytree falls back per documented behavior.
  • I've updated tool descriptions/schemas if I changed tool behavior — or N/A

This contribution was developed with AI assistance (Codex).

Changed files

  • hermes_cli/profiles.py (modified, +3/-1)
  • tests/hermes_cli/test_profiles.py (modified, +17/-0)

PR #11829: fix(profiles): pass symlinks=True to copytree to prevent RecursionError

Description (problem / solution / changelog)

Summary

hermes profile create <name> --clone-all raises RecursionError when the profile directory contains a symlink pointing to a parent directory. shutil.copytree follows symlinks by default, causing infinite recursion.

Changes

  • hermes_cli/profiles.py: add symlinks=True to the shutil.copytree call so symlinks are copied as symlinks rather than followed

Testing

Profile creation with a directory containing a circular symlink no longer crashes.

Closes #11560

Changed files

  • hermes_cli/profiles.py (modified, +6/-2)
  • scripts/release.py (modified, +1/-0)
RAW_BUFFERClick to expand / collapse

Bug

Running hermes profile create <name> --clone-all fails with a RecursionError in shutil.copytree when the profile directory contains a symlink that points to a parent directory.

Traceback

shutil.py _copytree → copytree → _copytree (infinite recursion) RecursionError: maximum recursion depth exceeded

Root cause

shutil.copytree is called without symlinks=True, so it follows symlinks rather than copying them as symlinks. If any symlink in ~/.hermes points to a parent directory, this causes infinite recursion.

Fix

Pass symlinks=True to the shutil.copytree call in the profile clone code.

Workaround

Use export/import instead: hermes profile export default ./backup.tar.gz hermes profile import ./backup.tar.gz newprofilename

extent analysis

TL;DR

Pass symlinks=True to the shutil.copytree call to prevent infinite recursion when cloning a profile with symlinks.

Guidance

  • Identify the shutil.copytree call in the profile clone code and modify it to include symlinks=True to correctly handle symlinks.
  • Verify that the modified code can clone a profile with symlinks without encountering a RecursionError.
  • As a temporary workaround, use the export/import method: hermes profile export default ./backup.tar.gz followed by hermes profile import ./backup.tar.gz newprofilename.
  • Test the fix with a profile containing a symlink that points to a parent directory to ensure the issue is resolved.

Example

import shutil
# Before
shutil.copytree(src, dst)
# After
shutil.copytree(src, dst, symlinks=True)

Notes

This fix assumes that the shutil.copytree call is the only place where symlinks are being copied. If there are other places where symlinks are being copied, they may also need to be modified.

Recommendation

Apply the workaround by using the export/import method until the fix can be implemented, as it provides a safe and reliable way to clone profiles with symlinks.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

hermes - ✅(Solved) Fix hermes profile --clone-all crashes with RecursionError on recursive symlink [2 pull requests, 1 comments, 2 participants]