sqlalchemy/alembic · error · CommandError
Error executing editor
Error message
Error executing editor (%s)
What it means
Raised as CommandError by open_in_editor when either _find_editor or the subprocess.check_call to launch the editor raises any exception (editor.py:31-35). This wraps the underlying error — which could be the editor binary not found, the editor exiting non-zero, the file path being invalid, or permission errors — into Alembic's CommandError with the original exception chained via 'from exc'.
Solutions
- Check the chained exception (raise ... from exc) for the root cause — run with --verbose or inspect the traceback.
- Verify the EDITOR/VISUAL env var points to a working editor: echo $EDITOR && $EDITOR --version.
- If the editor exited non-zero intentionally (e.g., vim :cq), fix the editor session or use a different editor.
- Ensure the migration file path is accessible and writable.
- Set EDITOR to a simple known-good editor like 'nano' or 'vim' to isolate the issue.
Example fix
# before: EDITOR points to a broken/custom wrapper export EDITOR='/usr/local/bin/my-broken-editor' alembic edit abc123 # after export EDITOR='vim' alembic edit abc123
Defensive patterns
Strategy: try-catch
Validate before calling
import os
from os.path import exists
def validate_editor_available(environ=None):
env = environ or os.environ
for var in ('EDITOR', 'VISUAL'):
if var in env and (exists(env[var]) or env[var]):
return True
from alembic.util.editor import _default_editors
for candidate in _default_editors():
for p in env.get('PATH', '').split(os.pathsep):
if exists(os.path.join(p, candidate)):
return True
return False Try / catch
from alembic.util.exc import CommandError
try:
util.open_in_editor(filepath)
except CommandError as e:
# inspect e.__cause__ for the underlying error
cause = e.__cause__
print(f"Editor failed: {cause}")
if isinstance(cause, OSError) and 'No suitable editor' in str(cause):
os.environ['EDITOR'] = 'vim'
util.open_in_editor(filepath)
else:
raise Prevention
- Always set EDITOR or VISUAL in your environment.
- Test that your editor can open a file non-interactively before using alembic edit.
- In CI, avoid interactive commands or set EDITOR to a no-op like 'true'.
When it happens
Trigger: Calling alembic edit <revision> (which calls util.open_in_editor at command.py:826) when the editor process fails. Also triggered by 'alembic revision' in edit mode. The try/except at editor.py:31-35 catches all Exception subclasses including OSError, CalledProcessError, and FileNotFoundError.
Common situations: The EDITOR/VISUAL env var points to a binary that crashes on the file. The editor exits with a non-zero status (e.g., vim :cq). The file path contains spaces or special characters not properly escaped. Permission denied on the migration file. Editor binary exists in EDITOR but fails at runtime.
Related errors
- No suitable editor found. Please set the "EDITOR" or…
- No context has been configured yet.
- Template rendering failed; see
- A plugin named is already registered
- Can not set dispatch function for object
AI-assisted analysis of sqlalchemy/alembic@5551b5d35f (2026-08-11).
Data as JSON: /api/errors/1df2bf42f5121016.
Report an issue: GitHub.
Appendix: source
Thrown at alembic/util/editor.py:35
"""
Opens the given file in a text editor. If the environment variable
``EDITOR`` is set, this is taken as preference.
Otherwise, a list of commonly installed editors is tried.
If no editor matches, an :py:exc:`OSError` is raised.
:param filename: The filename to open. Will be passed verbatim to the
editor command.
:param environ: An optional drop-in replacement for ``os.environ``. Used
mainly for testing.
"""
env = os.environ if environ is None else environ
try:
editor = _find_editor(env)
check_call([editor, filename])
except Exception as exc:
raise CommandError("Error executing editor (%s)" % (exc,)) from exc
def _find_editor(environ: Mapping[str, str]) -> str:
candidates = _default_editors()
for i, var in enumerate(("EDITOR", "VISUAL")):
if var in environ:
user_choice = environ[var]
if exists(user_choice):
return user_choice
if os.sep not in user_choice:
candidates.insert(i, user_choice)
for candidate in candidates:
path = _find_executable(candidate, environ)
if path is not None:
return path
raise OSError(
"No suitable editor found. Please set the "View on GitHub (pinned to 5551b5d35f)