{"record":{"id":"121f3371745a085c","repo":"crewAIInc/crewAI","slug":"failed-to-create-or-read-encryption-key","errorCode":null,"errorMessage":"Failed to create or read encryption key","messagePattern":"Failed to create or read encryption key","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"critical","filePath":"lib/crewai-core/src/crewai_core/token_manager.py","lineNumber":48,"sourceCode":"        self.fernet = Fernet(self.key)\n\n    def _get_or_create_key(self) -> bytes:\n        \"\"\"Get or create the encryption key.\"\"\"\n        key_filename: str = \"secret.key\"\n\n        key = self._read_secure_file(key_filename)\n        if key is not None and len(key) == _FERNET_KEY_LENGTH:\n            return key\n\n        new_key = Fernet.generate_key()\n        if self._atomic_create_secure_file(key_filename, new_key):\n            return new_key\n\n        key = self._read_secure_file(key_filename)\n        if key is not None and len(key) == _FERNET_KEY_LENGTH:\n            return key\n\n        raise RuntimeError(\"Failed to create or read encryption key\")\n\n    def save_tokens(self, access_token: str, expires_at: int) -> None:\n        \"\"\"Save the access token and its expiration time.\n\n        Args:\n            access_token: The access token to save.\n            expires_at: The UNIX timestamp of the expiration time.\n        \"\"\"\n        expiration_time = datetime.fromtimestamp(expires_at)\n        data = {\n            \"access_token\": access_token,\n            \"expiration\": expiration_time.isoformat(),\n        }\n        encrypted_data = self.fernet.encrypt(json.dumps(data).encode())\n        self._atomic_write_secure_file(self.file_path, encrypted_data)\n\n    def get_token(self) -> str | None:\n        \"\"\"Return the access token if valid and not expired, else None.\"\"\"","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/crewAIInc/crewAI/blob/754d7323beb2fd042e33444a115ea2d5a47193f0/lib/crewai-core/src/crewai_core/token_manager.py#L30-L66","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check write permission on the directory where secret.key is stored and chown/chmod it for the running user.","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).","On read-only filesystems, point the storage to a writable location or run as a user with write access.","Free disk space if the atomic create is failing due to ENOSPC."],"exampleFix":"# before: config dir owned by root, process runs as app user\n# chown -R appuser ~/.config/crewai\n\n# after: verify writability before constructing\nfrom pathlib import Path\nkey_dir = Path.home() / \".config\" / \"crewai\"\nkey_dir.mkdir(parents=True, exist_ok=True)\nassert os.access(key_dir, os.W_OK), f\"no write access to {key_dir}\"\ntm = TokenManager()","handlingStrategy":"try-catch","validationCode":"import os\nfrom pathlib import Path\n\ndef key_store_writable() -> bool:\n    d = Path(\".\")  # directory where secret.key is stored\n    return os.access(d, os.W_OK | os.R_OK)","typeGuard":null,"tryCatchPattern":"try:\n    tm = TokenManager()\nexcept RuntimeError as e:\n    if \"encryption key\" in str(e):\n        # fix permissions on the key directory / remove corrupt secret.key, then retry once after remediation\n        ...","preventionTips":["Ensure the process user owns the directory that stores secret.key (especially in Docker/CI).","Health-check key file at startup: exists => must be exactly 44 bytes.","Back up secret.key — deleting it makes existing tokens undecryptable."],"tags":["encryption","filesystem","permissions","credentials"],"backgroundTag":null,"analyzedSha":"754d7323beb2fd042e33444a115ea2d5a47193f0","analyzedAt":"2026-08-15T04:06:56.746Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}