trailofbits/algo · error · OSError

Failed to read private key file: {e}

Error message

Failed to read private key file: {e}

What it means

The module could not read the file given as private_key_path: the OS raised OSError (file not found, permission denied, or an I/O error) during open()/read(). The exception text is embedded in the message.

Source

Thrown at library/x25519_pubkey.py:77

                data = f.read()
            try:
                # First attempt: assume file contains base64 text data
                # Strip whitespace from edges for text files (safe for base64 strings)
                stripped_data = data.strip()
                base64.b64decode(stripped_data, validate=True)
                priv_b64 = stripped_data.decode()
            except (base64.binascii.Error, ValueError):
                # Second attempt: assume file contains raw binary data
                # CRITICAL: Do NOT strip raw binary data - X25519 keys can contain
                # whitespace-like bytes (0x09, 0x0A, etc.) that must be preserved
                # Stripping would corrupt the key and cause "got 31 bytes" errors
                if len(data) != 32:
                    module.fail_json(
                        msg=f"Private key file must be either base64 or exactly 32 raw bytes, got {len(data)} bytes"
                    )
                priv_b64 = base64.b64encode(data).decode()
        except OSError as e:
            module.fail_json(msg=f"Failed to read private key file: {e}")
    else:
        priv_b64 = module.params["private_key_b64"]

    # Validate input parameters
    if not priv_b64:
        module.fail_json(msg="No private key provided")

    try:
        priv_raw = base64.b64decode(priv_b64, validate=True)
    except Exception as e:
        module.fail_json(msg=f"Invalid base64 private key format: {e}")

    if len(priv_raw) != 32:
        module.fail_json(msg=f"Private key must decode to exactly 32 bytes, got {len(priv_raw)}")

    try:
        priv_key = x25519.X25519PrivateKey.from_private_bytes(priv_raw)
        pub_key = priv_key.public_key()

View on GitHub (pinned to 20e22a8715)

Solutions

  1. Check the path exists: ls -l <private_key_path>
  2. Fix ownership/permissions: sudo chown -R $USER configs/ && chmod 600 <keyfile>
  3. Use an absolute path in the task
  4. If generated in a previous task, verify the key-generation task actually ran and produced the file
Defensive patterns

Strategy: validation

Validate before calling

import os
p = '/path/to/priv.key'
assert os.path.isfile(p) and os.access(p, os.R_OK), f'cannot read {p}'

Type guard

def readable_file(p: str) -> bool:
    import os
    return bool(p) and os.path.isfile(p) and os.access(p, os.R_OK)

Prevention

When it happens

Trigger: private_key_path points to a nonexistent path, a file the Ansible user cannot read (bad ownership/mode on configs/), or a directory/IS-A-FILE error.

Common situations: Running ansible as a different user than the one that generated keys, wrong relative path (resolved relative to the play, not the roles dir), or configs/ owned by root.

Related errors


AI-assisted analysis of trailofbits/algo@20e22a8715 (2026-08-28). Data as JSON: /api/errors/561f13da759f8606. Report an issue: GitHub.