diff --git a/letta/schemas/environment_variables.py b/letta/schemas/environment_variables.py index 81abfa35..5db1c28a 100644 --- a/letta/schemas/environment_variables.py +++ b/letta/schemas/environment_variables.py @@ -1,11 +1,15 @@ +import traceback from typing import Optional from pydantic import Field, model_validator +from letta.log import get_logger from letta.schemas.enums import PrimitiveType from letta.schemas.letta_base import LettaBase, OrmMetadataBase from letta.schemas.secret import Secret +logger = get_logger(__name__) + # Base Environment Variable class EnvironmentVariableBase(OrmMetadataBase): @@ -28,11 +32,23 @@ class EnvironmentVariableBase(OrmMetadataBase): def sync_value_and_value_enc(self): """Sync deprecated `value` field with `value_enc` for backward compatibility.""" if self.value_enc and not self.value: + # ERROR: This should not happen - all code paths should populate value via async decryption + # Log error with stack trace to identify the caller that bypassed async decryption + logger.warning( + f"Sync decryption fallback triggered for env var key={self.key}. " + f"This indicates a code path that bypassed async decryption. Stack trace:\n{''.join(traceback.format_stack())}" + ) # Decrypt value_enc -> value (for API responses) plaintext = self.value_enc.get_plaintext() if plaintext: self.value = plaintext elif self.value and not self.value_enc: + # WARNING: This triggers sync encryption - should use async encryption where possible + # Log warning with stack trace to identify the caller + logger.warning( + f"Sync encryption fallback triggered for env var key={self.key}. " + f"This indicates a code path that bypassed async encryption. Stack trace:\n{''.join(traceback.format_stack())}" + ) # Encrypt value -> value_enc (for backward compat when value is provided directly) self.value_enc = Secret.from_plaintext(self.value) return self