ansible/ansible · error · AnsibleOptionsError

stdin was empty, not encrypting

Error message

stdin was empty, not encrypting

What it means

AnsibleOptionsError raised by the stdin-reading branch of encrypt_string (active when '-' is an arg, --encrypt-string-stdin-name is set, or no args/--name given) when sys.stdin.read() returns ''. Like the empty-prompt case, encrypting zero bytes from a declared stdin source is rejected rather than emitting an empty encrypted string.

Source

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

            else:
                msg = "String to encrypt:"

            prompt_response = display.prompt(msg, private=hide_input)

            if prompt_response == '':
                raise AnsibleOptionsError('The plaintext provided from the prompt was empty, not encrypting')

            b_plaintext = to_bytes(prompt_response)
            b_plaintext_list.append((b_plaintext, self.FROM_PROMPT, name))

        # read from stdin
        if self.encrypt_string_read_stdin:
            if sys.stdout.isatty():
                display.display("Reading plaintext input from stdin. (ctrl-d to end input, twice if your content does not already have a newline)", stderr=True)

            stdin_text = sys.stdin.read()
            if stdin_text == '':
                raise AnsibleOptionsError('stdin was empty, not encrypting')

            if sys.stdout.isatty() and not stdin_text.endswith("\n"):
                display.display("\n")

            b_plaintext = to_bytes(stdin_text)

            # defaults to None
            name = context.CLIARGS['encrypt_string_stdin_name']
            b_plaintext_list.append((b_plaintext, self.FROM_STDIN, name))

        # use any leftover args as strings to encrypt
        # Try to match args up to --name options
        if context.CLIARGS.get('encrypt_string_names', False):
            name_and_text_list = list(zip(context.CLIARGS['encrypt_string_names'], args))

            # Some but not enough --name's to name each var
            if len(args) > len(name_and_text_list):
                # Trying to avoid ever showing the plaintext in the output, so this warning is vague to avoid that.

View on GitHub (pinned to 9cf16a4aca)

Solutions

  1. Ensure the pipeline actually produces content: check the upstream command's output first
  2. Guard in scripts: test -n "$SECRET" && printf '%s' "$SECRET" | ansible-vault encrypt_string --stdin-name myvar
  3. If the value can legitimately be empty, decide explicitly — ansible-vault will not encrypt '' and you should skip the call

Example fix

# before
printf '%s' "$MY_TOKEN" | ansible-vault encrypt_string --stdin-name token  # MY_TOKEN unset

# after
test -n "$MY_TOKEN" || { echo 'MY_TOKEN is empty' >&2; exit 1; }
printf '%s' "$MY_TOKEN" | ansible-vault encrypt_string --stdin-name token
Defensive patterns

Strategy: validation

Validate before calling

import sys

data = sys.stdin.read()
if data == '':
    raise SystemExit('stdin empty; upstream producer failed or secret unset')
# only then pipe into: ansible-vault encrypt_string --stdin-name NAME

Prevention

When it happens

Trigger: `echo -n '' | ansible-vault encrypt_string --stdin-name myvar`, redirecting an empty file (`ansible-vault encrypt_string - < /dev/null`), or a pipeline upstream that produced no output.

Common situations: CI jobs where the secret variable is unset so the pipe carries an empty string; a previous command in the pipeline failed silently and emitted nothing.

Related errors


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