{"repo":"NousResearch/hermes-agent","public_id":"18787","display_label":"Docker backend bind-mount path translation issues","summary":"These items concern incorrect path resolution for bind mounts in Docker-based backends, including credential files, skill directories, cache paths, and document paths that reference container-internal paths instead of host-visible paths. Scenarios covered include sibling containers, Docker-out-of-Docker (DooD), and remote terminal backends.","subsystem":"environments","counts":{"members":16,"open_issues":3,"open_issues_distinct":3,"open_prs":3,"closed_issues":2,"merged_prs":1,"attempt_prs":10},"landed_fix":{"status":"merged_to_main","basis":"structural","pr":21184},"resolution":{"state":"partially_resolved","by":"pr-21184"},"freshness":{"state":"verified","last_verified_at":"2026-05-07T12:02:27+00:00","is_stale":false},"grouping":{"backbone":"STRUCTURAL_PARTIAL","link_density":0.10666666666666665},"fix_edges":[{"issue_number":18787,"pr_number":21184,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true}],"members":[{"kind":"issue","number":18787,"title":"[Bug]: Inbound document host paths not translated to container paths under Docker backend","url":null,"state":"closed"},{"kind":"issue","number":24483,"title":"Docker sandbox fails with exit 125 when container_persistent=true and credential files are registered","url":null,"state":"closed"},{"kind":"issue","number":40368,"title":"[Bug] Docker backend generates container-internal paths for sandbox bind mounts when using host Docker socket (DooD on Unraid)","url":null,"state":"open"},{"kind":"issue","number":41541,"title":"${HERMES_SKILL_DIR} resolves to host paths on remote terminal backends","url":null,"state":"open"},{"kind":"issue","number":50798,"title":"Sandbox skill + cache bind-mounts are empty under docker-compose self-host (sibling-container bind source uses in-container HERMES_HOME, not the host path)","url":null,"state":"open"},{"kind":"pr","number":18794,"title":"fix(gateway): translate document cache paths for docker","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#18787","number":18787}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false},{"kind":"pr","number":19048,"title":"fix(gateway): translate inbound document host paths to container paths for Docker backend","url":null,"state":"closed","close_reason":{"refs":[{"kind":"commit","value":"6d1b61d63","number":0},{"kind":"pr","value":"#18787","number":18787},{"kind":"pr","value":"#19610","number":19610}],"category":"duplicate_of","implication":"duplicate"},"merged":false},{"kind":"pr","number":19610,"title":"fix(gateway): translate inbound cache paths to container paths for Docker backend","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#19048","number":19048}],"category":"duplicate_of","implication":"duplicate"},"merged":false},{"kind":"pr","number":21184,"title":"fix(gateway): translate inbound document host paths to container paths for Docker backend (salvage #19048)","url":null,"state":"merged","close_reason":{"category":"merged","implication":"superseded"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["gateway/run.py","tools/credential_files.py"],"fix_hunks":[{"file":"gateway/run.py","hunks":["@@ -5757,6 +5757,7 @@ async def _prepare_inbound_message_text(\n \n         if event.media_urls and event.message_type == MessageType.DOCUMENT:\n             import mimetypes as _mimetypes\n+            from tools.credential_files import to_agent_visible_cache_path\n \n             _TEXT_EXTENSIONS = {\".txt\", \".md\", \".csv\", \".log\", \".json\", \".xml\", \".yaml\", \".yml\", \".toml\", \".ini\", \".cfg\"}\n             for i, path in enumerate(event.media_urls):","@@ -5777,16 +5778,21 @@ async def _prepare_inbound_message_text(\n                 display_name = parts[2] if len(parts) >= 3 else basename\n                 display_name = re.sub(r'[^\\w.\\- ]', '_', display_name)\n \n+                # Translate host cache path to in-container path if running under Docker backend.\n+                # This ensures the agent receives a path it can open inside its sandbox, as the\n+                # cache directories are auto-mounted at /root/.hermes/cache/* by get_cache_directory_mounts().\n+                agent_path = to_agent_visible_cache_path(path)\n+\n                 if mtype.startswith(\"text/\"):\n                     context_note = (\n                         f\"[The user sent a text document: '{display_name}'. \"\n                         f\"Its content has been included below. \"\n-                        f\"The file is also saved at: {path}]\"\n+                        f\"The file is also saved at: {agent_path}]\"\n                     )\n                 else:\n                     context_note = (\n                         f\"[The user sent a document: '{display_name}'. \"\n-                        f\"The file is saved at: {path}. \"\n+                        f\"The file is saved at: {agent_path}. \"\n                         f\"Ask the user what they'd like you to do with it.]\"\n                     )\n                 message_text = f\"{context_note}\\n\\n{message_text}\""]},{"file":"tools/credential_files.py","hunks":["@@ -374,6 +374,34 @@ def get_cache_directory_mounts(\n     return mounts\n \n \n+def to_agent_visible_cache_path(\n+    host_path: str,\n+    container_base: str = \"/root/.hermes\",\n+) -> str:\n+    \"\"\"Translate a host cache path to its mounted path inside the sandbox.\n+\n+    Returns the input unchanged if it is not under any auto-mounted cache\n+    directory, or if the active terminal backend does not require path\n+    translation (only Docker for now).\n+    \"\"\"\n+    # Only Docker backend requires translation at this time.  Other backends\n+    # (Modal, Daytona, Vercel) use different mount semantics and will be\n+    # addressed separately if needed.  Backend is identified by TERMINAL_ENV\n+    # (same env var tools/terminal_tool.py reads in _get_environment_config).\n+    if os.environ.get(\"TERMINAL_ENV\", \"local\") != \"docker\":\n+        return host_path\n+\n+    path = Path(host_path)\n+    for mount in get_cache_directory_mounts(container_base=container_base):\n+        host_dir = Path(mount[\"host_path\"])\n+        try:\n+            rel = path.relative_to(host_dir)\n+            return str(Path(mount[\"container_path\"]) / rel)\n+        except ValueError:\n+            continue\n+    return host_path\n+\n+\n def iter_cache_files(\n     container_base: str = \"/root/.hermes\",\n ) -> List[Dict[str, str]]:\n"]}]},{"kind":"pr","number":24555,"title":"fix(docker): remove corrupted sandbox dir before credential bind-mount","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#24490","number":24490},{"kind":"pr","value":"#24483","number":24483},{"kind":"pr","value":"#24490","number":24490}],"category":"superseded_by_pr","implication":"superseded"},"merged":false},{"kind":"pr","number":40404,"title":"fix(docker): rewrite DooD auto-mount sources via host_cwd","url":null,"state":"open","close_reason":{"category":"uncategorized","implication":"needs_llm"},"merged":false},{"kind":"pr","number":41561,"title":"fix(skills): resolve ${HERMES_SKILL_DIR} to backend-visible paths on remote terminal backends","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#41541","number":41541}],"category":"volume_quota","implication":"active"},"merged":false},{"kind":"pr","number":41562,"title":"fix(skills): resolve skill directory paths for non-local terminal backends","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#41561","number":41561},{"kind":"pr","value":"#41562","number":41562},{"kind":"pr","value":"#41561","number":41561},{"kind":"pr","value":"#41561","number":41561}],"category":"superseded_by_pr","implication":"superseded"},"merged":false,"is_fix_pr":true,"fix_tier":"suggested","fix_detail_withheld":true},{"kind":"pr","number":50874,"title":"fix(docker): translate container paths to host paths for sibling mounts","url":null,"state":"closed","close_reason":{"category":"uncategorized","implication":"needs_llm"},"merged":false},{"kind":"pr","number":75418,"title":"fix(docker): resolve sibling bind sources on host","url":null,"state":"open","close_reason":{"category":"implemented_elsewhere","implication":"superseded"},"merged":false},{"kind":"pr","number":80879,"title":"fix(skills): resolve skill paths to backend-visible locations on remote terminal backends","url":null,"state":"open","close_reason":{"category":"uncategorized","implication":"needs_llm"},"merged":false}],"trust":{"default_tier":"verified","includes_suggested":false}}