ansible/ansible · error · AnsibleOptionsError

not a tty, editor cannot be opened

Error message

not a tty, editor cannot be opened

What it means

AnsibleOptionsError raised by VaultCLI.execute_create when stdout is not a TTY and --skip-tty-check was not passed. `ansible-vault create` needs to spawn $EDITOR interactively; without a terminal the editor cannot run, so Ansible aborts instead of hanging or corrupting the file. (Note the check is on stdout being a tty, so piping output also triggers it.)

Source

Thrown at lib/ansible/cli/vault.py:457

            display.display("Reading ciphertext input from stdin", stderr=True)

        for f in context.CLIARGS['args'] or ['-']:
            self.editor.decrypt_file(f, output_file=context.CLIARGS['output_file'])

        if sys.stdout.isatty():
            display.display("Decryption successful", stderr=True)

    def execute_create(self):
        """ create and open a file in an editor that will be encrypted with the provided vault secret when closed"""

        if len(context.CLIARGS['args']) != 1:
            raise AnsibleOptionsError("ansible-vault create can take only one filename argument")

        if sys.stdout.isatty() or context.CLIARGS['skip_tty_check']:
            self.editor.create_file(context.CLIARGS['args'][0], self.encrypt_secret,
                                    vault_id=self.encrypt_vault_id)
        else:
            raise AnsibleOptionsError("not a tty, editor cannot be opened")

    def execute_edit(self):
        """ open and decrypt an existing vaulted file in an editor, that will be encrypted again when closed"""
        for f in context.CLIARGS['args']:
            self.editor.edit_file(f)

    def execute_view(self):
        """ open, decrypt and view an existing vaulted file using a pager using the supplied vault secret """

        for f in context.CLIARGS['args']:
            # Note: vault should return byte strings because it could encrypt
            # and decrypt binary files.  We are responsible for changing it to
            # unicode here because we are displaying it and therefore can make
            # the decision that the display doesn't have to be precisely what
            # the input was (leave that to decrypt instead)
            plaintext = self.editor.plaintext(f)
            self.pager(to_text(plaintext))

View on GitHub (pinned to 9cf16a4aca)

Solutions

  1. For automation, avoid create entirely: write the plaintext then `ansible-vault encrypt` it, or pipe to `ansible-vault encrypt_string --stdin-name`
  2. If a real terminal exists upstream (e.g. ssh), allocate it: ssh -t, docker exec -it
  3. As a last resort, --skip-tty-check lets it proceed, but the editor still needs to work non-interactively (e.g. EDITOR=true with content pre-supplied)

Example fix

# before (CI job)
ansible-vault create secret.yml  # fails: not a tty

# after
printf 'my_secret: value\n' > secret.yml
ansible-vault encrypt --vault-id prompt secret.yml
Defensive patterns

Strategy: validation

Validate before calling

import sys, os

def create_is_safe() -> bool:
    return sys.stdout.isatty() or bool(os.environ.get('ANSIBLE_VAULT_SKIP_TTY_CHECK'))

if not create_is_safe():
    # non-interactive: write plaintext, then encrypt
    pass

Prevention

When it happens

Trigger: Running `ansible-vault create f.yml` inside CI, cron, a non-interactive SSH session without -t, or with output piped/redirected, without --skip-tty-check.

Common situations: Automation/CI attempts to use `create`; docker exec without -t; nohup'd scripts. Also $EDITOR unset or nonfunctional in minimal environments.

Related errors


AI-assisted analysis of ansible/ansible@9cf16a4aca (2026-08-15). Data as JSON: /api/errors/14cb9646c65cd630. Report an issue: GitHub.