langchain-ai/langgraph · error · ValueError
LANGGRAPH_AES_KEY environment variable is not set.
Error message
LANGGRAPH_AES_KEY environment variable is not set.
What it means
Configuration guard in EncryptedSerializer.from_pycryptodome_aes: no 'key' keyword was supplied and the fallback environment variable LANGGRAPH_AES_KEY is unset (os.getenv returned None), so there is no AES key material to derive the cipher. The input at fault is the missing key configuration, not the environment or code.
Solutions
- Export the variable: export LANGGRAPH_AES_KEY=<16/24/32-byte key material>
- Or pass the key directly: EncryptedSerializer.from_pycryptodome_aes(key=b'...')
- Load the key from a secret manager and pass it explicitly instead of relying on the environment
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at libs/checkpoint/langgraph/checkpoint/serde/encrypted.py:56 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of langchain-ai/langgraph@38031739e5 (2026-08-26).
Data as JSON: /api/errors/20a0a63e86f6dbef.
Report an issue: GitHub.
Appendix: source
Thrown at libs/checkpoint/langgraph/checkpoint/serde/encrypted.py:56
@classmethod
def from_pycryptodome_aes(
cls, serde: SerializerProtocol = JsonPlusSerializer(), **kwargs: Any
) -> "EncryptedSerializer":
"""Create an `EncryptedSerializer` using AES encryption."""
try:
from Crypto.Cipher import AES
except ImportError:
raise ImportError(
"Pycryptodome is not installed. Please install it with `pip install pycryptodome`."
) from None
# check if AES key is provided
if "key" in kwargs:
key: bytes = kwargs.pop("key")
else:
key_str = os.getenv("LANGGRAPH_AES_KEY")
if key_str is None:
raise ValueError("LANGGRAPH_AES_KEY environment variable is not set.")
key = key_str.encode()
if len(key) not in (16, 24, 32):
raise ValueError("LANGGRAPH_AES_KEY must be 16, 24, or 32 bytes long.")
# set default mode to EAX if not provided
if kwargs.get("mode") is None:
kwargs["mode"] = AES.MODE_EAX
class PycryptodomeAesCipher(CipherProtocol):
def encrypt(self, plaintext: bytes) -> tuple[str, bytes]:
cipher = AES.new(key, **kwargs)
ciphertext, tag = cipher.encrypt_and_digest(plaintext)
return "aes", cipher.nonce + tag + ciphertext
def decrypt(self, ciphername: str, ciphertext: bytes) -> bytes:
assert ciphername == "aes", f"Unsupported cipher: {ciphername}"
nonce = ciphertext[:16]
tag = ciphertext[16:32]View on GitHub (pinned to 38031739e5)