{"record":{"id":"4f4287d6cfd8e7e5","repo":"NousResearch/hermes-agent","slug":"write-denied-path-requires-interactive-approv","errorCode":null,"errorMessage":"Write denied: '{path}' requires interactive approval and cannot be written through the ACP file bridge.","messagePattern":"Write denied: '(.+?)' requires interactive approval and cannot be written through the ACP file bridge\\.","errorType":"exception","errorClass":"PermissionError","httpStatus":null,"severity":"error","filePath":"agent/copilot_acp_client.py","lineNumber":742,"sourceCode":"                    \"jsonrpc\": \"2.0\",\n                    \"id\": message_id,\n                    \"result\": {\n                        \"content\": content,\n                    },\n                }\n            except Exception as exc:\n                response = _jsonrpc_error(message_id, -32602, str(exc))\n        elif method == \"fs/write_text_file\":\n            try:\n                path = _ensure_path_within_cwd(str(params.get(\"path\") or \"\"), cwd)\n                denied = get_write_denied_error(str(path))\n                if denied:\n                    raise PermissionError(denied)\n                # Approval-gated paths (e.g. ~/.ssh/config) are not hard-denied\n                # for interactive tools, but the ACP shim has no human channel\n                # to confirm the write — fail closed here.\n                if is_write_approval_required(str(path)):\n                    raise PermissionError(\n                        f\"Write denied: '{path}' requires interactive approval \"\n                        \"and cannot be written through the ACP file bridge.\"\n                    )\n                path.parent.mkdir(parents=True, exist_ok=True)\n                path.write_text(str(params.get(\"content\") or \"\"), encoding=\"utf-8\")\n                response = {\n                    \"jsonrpc\": \"2.0\",\n                    \"id\": message_id,\n                    \"result\": None,\n                }\n            except Exception as exc:\n                response = _jsonrpc_error(message_id, -32602, str(exc))\n        else:\n            response = _jsonrpc_error(\n                message_id,\n                -32601,\n                f\"ACP client method '{method}' is not supported by Hermes yet.\",\n            )","sourceCodeStart":724,"sourceCodeEnd":760,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/agent/copilot_acp_client.py#L724-L760","documentation":"An fs/write_text_file request from the Copilot ACP process targeted a path that passes the hard deny-list but is approval-gated (e.g. ~/.ssh/config). The ACP shim has no interactive human channel to confirm such writes, so it fails closed with this PermissionError instead of writing silently — interactive tools would prompt, but the bridge cannot.","triggerScenarios":"is_write_approval_required(str(path)) returns true for the resolved write target — paths considered sensitive enough to need a human yes/no (SSH config and similar). Reached only after _ensure_path_within_cwd and get_write_denied_error both pass.","commonSituations":"Copilot ACP agent trying to modify SSH config or other approval-gated files inside the session workspace scope; users expecting the ACP bridge to behave like the interactive CLI's approve-on-write flow.","solutions":["Let the agent write to a neutral file, then apply the change to the gated path yourself.","Make the modification manually through the interactive CLI (hermes), which can surface the approval prompt.","If the path should not be gated, review the approval-required list configuration — do not bypass the guard for credential-adjacent files."],"exampleFix":"# before — Copilot ACP tries to edit directly\nwrite ~/.ssh/config  → PermissionError (no approval channel)\n# after — agent proposes, human applies\ncp config.draft ~/.ssh/config   # operator-run, reviewed","handlingStrategy":"validation","validationCode":"# Before routing an ACP write, mirror the guard:\nfrom agent.file_safety import is_write_approval_required, get_write_denied_error\n\ndef acp_writable(path: str) -> bool:\n    return (get_write_denied_error(path) is None\n            and not is_write_approval_required(path))","typeGuard":null,"tryCatchPattern":"try:\n    handle_fs_write(params)\nexcept PermissionError as e:\n    if 'requires interactive approval' in str(e):\n        # write to a draft file instead; apply manually via interactive CLI\n        ...","preventionTips":["Direct ACP agents to draft files for gated paths; apply changes manually","Use the interactive CLI when approval-gated writes are needed","Do not attempt to bypass the approval gate — it protects sensitive configs"],"tags":["copilot","acp","security","write-protection"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}