fix: prevent SessionStateEncoder crash from non-serializable datetime on args
Root cause: bot_flow.py injected configs.args.global_start_time = datetime.now() which polluted the args namespace. SessionStateEncoder blindly serialized args.__dict__ via json.dump, which crashed mid-write on the datetime object, leaving sessions.json truncated/corrupt. Every subsequent restart failed. Fixes: - Remove global_start_time from configs.args (only lives on engine class vars) - Harden SessionStateEncoder with _sanitize_value() to convert any non-JSON-serializable type (datetime, timedelta, arbitrary objects) to strings - Add test_system_session_persistence.py with 4 tests covering the exact production crash scenario (datetime injection → json.dumps → round-trip) - Fix test_engine_timeout.py broken GoalExecutor module reference
This commit is contained in:
@@ -307,7 +307,6 @@ def start_bot(**kwargs):
|
||||
|
||||
try:
|
||||
bot_start_time = datetime.now()
|
||||
configs.args.global_start_time = bot_start_time
|
||||
max_runtime = getattr(configs.args, "max_runtime_minutes", None)
|
||||
|
||||
dopamine.global_start_time = bot_start_time
|
||||
|
||||
@@ -277,7 +277,29 @@ class SessionState:
|
||||
|
||||
|
||||
class SessionStateEncoder(JSONEncoder):
|
||||
"""JSON encoder for SessionState that is crash-proof against non-serializable types."""
|
||||
|
||||
_SAFE_TYPES = (str, int, float, bool, type(None))
|
||||
|
||||
@classmethod
|
||||
def _sanitize_value(cls, value):
|
||||
"""Convert any non-JSON-serializable value to a safe string representation."""
|
||||
if isinstance(value, cls._SAFE_TYPES):
|
||||
return value
|
||||
if isinstance(value, datetime):
|
||||
return value.isoformat()
|
||||
if isinstance(value, dict):
|
||||
return {k: cls._sanitize_value(v) for k, v in value.items()}
|
||||
if isinstance(value, (list, tuple)):
|
||||
return [cls._sanitize_value(v) for v in value]
|
||||
# Last resort: stringify unknown objects to prevent json.dump mid-write crashes
|
||||
return str(value)
|
||||
|
||||
def default(self, session_state: SessionState):
|
||||
# Sanitize args dict — never trust raw __dict__, it may contain datetime or other garbage
|
||||
raw_args = session_state.args.__dict__ if hasattr(session_state.args, "__dict__") else {}
|
||||
safe_args = {k: self._sanitize_value(v) for k, v in raw_args.items()}
|
||||
|
||||
return {
|
||||
"id": session_state.id,
|
||||
"total_interactions": sum(session_state.totalInteractions.values()),
|
||||
@@ -291,7 +313,7 @@ class SessionStateEncoder(JSONEncoder):
|
||||
"total_scraped": session_state.totalScraped,
|
||||
"start_time": str(session_state.startTime),
|
||||
"finish_time": str(session_state.finishTime),
|
||||
"args": session_state.args.__dict__,
|
||||
"args": safe_args,
|
||||
"profile": {
|
||||
"posts": session_state.my_posts_count,
|
||||
"followers": session_state.my_followers_count,
|
||||
|
||||
Reference in New Issue
Block a user