crewAIInc/crewAI · critical · RuntimeError

Failed to create or read encryption key

Error message

Failed to create or read encryption key

What it means

Raised by TokenManager._get_or_create_key after three attempts fail: reading an existing secret.key returned None or a value whose length is not 44 bytes (Fernet key length), atomically creating a new key file failed, and re-reading still failed. This means the key storage location is unwritable or unreadable — permissions, read-only filesystem, full disk, or a corrupt existing secret.key of the wrong size.

Solutions

  1. Check write permission on the directory where secret.key is stored and chown/chmod it for the running user.
  2. If secret.key exists but is corrupt, back it up and delete it so a fresh 44-byte key can be created (note: tokens encrypted with the old key become unrecoverable).
  3. On read-only filesystems, point the storage to a writable location or run as a user with write access.
  4. Free disk space if the atomic create is failing due to ENOSPC.

Example fix

# before: config dir owned by root, process runs as app user
# chown -R appuser ~/.config/crewai

# after: verify writability before constructing
from pathlib import Path
key_dir = Path.home() / ".config" / "crewai"
key_dir.mkdir(parents=True, exist_ok=True)
assert os.access(key_dir, os.W_OK), f"no write access to {key_dir}"
tm = TokenManager()
Defensive patterns

Strategy: try-catch

Validate before calling

import os
from pathlib import Path

def key_store_writable() -> bool:
    d = Path(".")  # directory where secret.key is stored
    return os.access(d, os.W_OK | os.R_OK)

Try / catch

try:
    tm = TokenManager()
except RuntimeError as e:
    if "encryption key" in str(e):
        # fix permissions on the key directory / remove corrupt secret.key, then retry once after remediation
        ...

Prevention

When it happens

Trigger: Instantiating TokenManager(...) when (a) secret.key exists but is truncated/corrupt (len != 44), and (b) _atomic_create_secure_file cannot replace it because the directory is read-only, owned by another user, or the disk is full; also in sandboxes that deny writes to the key directory.

Common situations: Running the CLI in Docker/CI as a user without write access to the config dir; a partially-written key from a killed process; NFS/home-dir quota issues; running with a read-only mounted config directory.

Related errors


AI-assisted analysis of crewAIInc/crewAI@754d7323be (2026-08-15). Data as JSON: /api/errors/121f3371745a085c. Report an issue: GitHub.

Appendix: source

Thrown at lib/crewai-core/src/crewai_core/token_manager.py:48

        self.fernet = Fernet(self.key)

    def _get_or_create_key(self) -> bytes:
        """Get or create the encryption key."""
        key_filename: str = "secret.key"

        key = self._read_secure_file(key_filename)
        if key is not None and len(key) == _FERNET_KEY_LENGTH:
            return key

        new_key = Fernet.generate_key()
        if self._atomic_create_secure_file(key_filename, new_key):
            return new_key

        key = self._read_secure_file(key_filename)
        if key is not None and len(key) == _FERNET_KEY_LENGTH:
            return key

        raise RuntimeError("Failed to create or read encryption key")

    def save_tokens(self, access_token: str, expires_at: int) -> None:
        """Save the access token and its expiration time.

        Args:
            access_token: The access token to save.
            expires_at: The UNIX timestamp of the expiration time.
        """
        expiration_time = datetime.fromtimestamp(expires_at)
        data = {
            "access_token": access_token,
            "expiration": expiration_time.isoformat(),
        }
        encrypted_data = self.fernet.encrypt(json.dumps(data).encode())
        self._atomic_write_secure_file(self.file_path, encrypted_data)

    def get_token(self) -> str | None:
        """Return the access token if valid and not expired, else None."""

View on GitHub (pinned to 754d7323be)