{"record":{"id":"076b8d94ff0355a0","repo":"langchain-ai/deepagents","slug":"external-editor-failed","errorCode":null,"errorMessage":"External editor failed","messagePattern":"External editor failed","errorType":"exception","errorClass":"ExternalEditorError","httpStatus":null,"severity":"error","filePath":"libs/code/deepagents_code/editor.py","lineNumber":198,"sourceCode":"        # Strip exactly one so the cursor lands on content, not a blank line,\n        # while preserving any intentional trailing newlines the user added.\n        edited = edited.removesuffix(\"\\n\")\n\n        # Chat composition historically treats a blank result as cancellation;\n        # callers with their own submit-time validation may opt in to preserving it.\n        if not allow_empty and not edited.strip():\n            return None\n\n    except FileNotFoundError as exc:\n        if raise_on_error:\n            msg = \"External editor executable or temporary file was not found\"\n            raise ExternalEditorError(msg) from exc\n        return None\n    except Exception as exc:\n        logger.warning(\"Editor failed\", exc_info=True)\n        if raise_on_error:\n            msg = \"External editor failed\"\n            raise ExternalEditorError(msg) from exc\n        return None\n    else:\n        return edited\n    finally:\n        if tmp_path is not None:\n            with contextlib.suppress(OSError):\n                Path(tmp_path).unlink(missing_ok=True)\n","sourceCodeStart":180,"sourceCodeEnd":206,"githubUrl":"https://github.com/langchain-ai/deepagents/blob/a1af029e6e73cb17c36bff823d227747b28e91e1/libs/code/deepagents_code/editor.py#L180-L206","documentation":"The external editor process ran but failed for a reason other than a missing file — non-zero exit, crash, missing execute permission, or unusable terminal/display environment. The original exception is logged at warning level with a traceback (`logger.warning(\"Editor failed\", exc_info=True)`), and with `raise_on_error=True` it is re-raised as `ExternalEditorError` with this generic message, chained to the cause.","triggerScenarios":"`open_in_editor(raise_on_error=True)` where launching or awaiting the editor subprocess raises any exception besides `FileNotFoundError`: editor exits non-zero, is killed, lacks +x permission, or cannot open its display.","commonSituations":"GUI editor launched over SSH with no display ('Cannot open display'); editor script exiting 1 due to its own config errors; permission denied on the editor binary; editor killed by OOM or a timeout.","solutions":["Check the app logs for the 'Editor failed' warning — it contains the underlying exception and traceback.","Run the editor command manually against a scratch file to reproduce its own error output.","For remote sessions use a terminal editor or set up X/SSH display forwarding.","Restore execute permission (`chmod +x`) on the editor binary if permission was denied."],"exampleFix":"// before\nEDITOR=gui-editor over SSH (no DISPLAY) -> ExternalEditorError\n// after\nexport EDITOR=vim  # terminal-capable editor","handlingStrategy":"try-catch","validationCode":"import os, shutil\n\ndef can_launch_editor(cmd: list[str]) -> bool:\n    return bool(cmd) and shutil.which(cmd[0]) is not None and os.access(cmd[0], os.X_OK)","typeGuard":null,"tryCatchPattern":"try:\n    edited = open_in_editor(text, raise_on_error=True)\nexcept ExternalEditorError as exc:\n    logger.error(\"Editor failed: %r (cause: %r)\", exc, exc.__cause__)\n    edited = None","preventionTips":["Test the editor command manually in the deployment environment.","Use a terminal editor remotely or set up display forwarding.","Check the app's 'Editor failed' warning log for the underlying traceback.","Keep the editor binary executable and its own configuration valid."],"tags":["editor","subprocess","process-failure"],"backgroundTag":"editor-process-failed","analyzedSha":"a1af029e6e73cb17c36bff823d227747b28e91e1","analyzedAt":"2026-08-29T11:43:24.718Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}