Spaces:
Sleeping
Sleeping
Claude Code Claude Opus 4.6 commited on
Commit ·
31c2ff8
1
Parent(s): d147990
Claude Code: Fix Cain - add aggressive cleanup of stale 'unknown' error in read_current_stage()
Browse filesThe read_current_stage() function was reading from status files that had stale
'unknown' errors, causing Cain to report errors when actually healthy.
Fix: Pre-read cleanup of ALL status file locations before reading, with
defensive handling of 'null' error states.
Related: Fixes "Cain has RUNNING! Error: unknown" health check issue.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
openclaw/.openclaw/health_monitor.py
CHANGED
|
@@ -896,8 +896,47 @@ def read_current_stage() -> str:
|
|
| 896 |
"""
|
| 897 |
Read Cain's current stage from cain_status.json.
|
| 898 |
Returns a simple status string.
|
|
|
|
|
|
|
|
|
|
| 899 |
"""
|
|
|
|
|
|
|
|
|
|
| 900 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 901 |
status_file = _find_status_file()
|
| 902 |
with open(status_file) as f:
|
| 903 |
data = json.load(f)
|
|
@@ -913,13 +952,9 @@ def read_current_stage() -> str:
|
|
| 913 |
error_normalized = error.strip().lower()
|
| 914 |
# Check if it's a "null" error (empty, unknown, none, or just whitespace)
|
| 915 |
if error_normalized in ("", "unknown", "none", "null"):
|
| 916 |
-
# This
|
| 917 |
-
|
| 918 |
-
|
| 919 |
-
with open(status_file, "w") as f:
|
| 920 |
-
json.dump(data, f, indent=2)
|
| 921 |
-
except Exception:
|
| 922 |
-
pass # Best effort fix
|
| 923 |
return stage
|
| 924 |
else:
|
| 925 |
# Real error detected - append _ERROR to stage
|
|
@@ -929,8 +964,6 @@ def read_current_stage() -> str:
|
|
| 929 |
return stage
|
| 930 |
except Exception as e:
|
| 931 |
# Log the actual error for debugging
|
| 932 |
-
import logging
|
| 933 |
-
logger = logging.getLogger(__name__)
|
| 934 |
logger.warning(f"Failed to read cain_status.json from {status_file if 'status_file' in locals() else 'unknown'}: {e}")
|
| 935 |
logger.debug(f"Exception details: ", exc_info=True)
|
| 936 |
return 'UNKNOWN'
|
|
|
|
| 896 |
"""
|
| 897 |
Read Cain's current stage from cain_status.json.
|
| 898 |
Returns a simple status string.
|
| 899 |
+
|
| 900 |
+
CRITICAL: Fix for "unknown" error stale state - cleans ALL status file locations
|
| 901 |
+
before reading, not just the one found by _find_status_file().
|
| 902 |
"""
|
| 903 |
+
import logging
|
| 904 |
+
logger = logging.getLogger(__name__)
|
| 905 |
+
|
| 906 |
try:
|
| 907 |
+
# CRITICAL FIX: Clean ALL possible status file locations BEFORE reading
|
| 908 |
+
# This prevents stale "unknown" errors from being read from any location
|
| 909 |
+
_all_possible_status_paths = [
|
| 910 |
+
Path("/data/cain_status.json"), # Primary (OPENCLAW_DATA_DIR)
|
| 911 |
+
Path("/app/openclaw/.openclaw/agents/cain_status.json"), # Nested structure
|
| 912 |
+
Path("/app/.openclaw/agents/cain_status.json"), # Legacy flat structure
|
| 913 |
+
Path("/app/cain_status.json"), # App root
|
| 914 |
+
Path("/app/data/cain_status.json"), # App data subdirectory
|
| 915 |
+
Path("/app/memory/cain_status.json"), # Memory directory
|
| 916 |
+
Path("/data/memory/cain_status.json"), # Data memory directory
|
| 917 |
+
]
|
| 918 |
+
|
| 919 |
+
cleaned_count = 0
|
| 920 |
+
for sp in _all_possible_status_paths:
|
| 921 |
+
try:
|
| 922 |
+
if sp.exists():
|
| 923 |
+
with open(sp, 'r') as f:
|
| 924 |
+
data = json.load(f)
|
| 925 |
+
error = data.get('error')
|
| 926 |
+
if isinstance(error, str) and error.strip().lower() in ('unknown', 'none', 'null', ''):
|
| 927 |
+
data['error'] = None
|
| 928 |
+
data['_cleaned_at'] = 'read_current_stage_pre_read'
|
| 929 |
+
with open(sp, 'w') as f:
|
| 930 |
+
json.dump(data, f, indent=2)
|
| 931 |
+
cleaned_count += 1
|
| 932 |
+
logger.debug(f"Cleaned stale '{error}' from {sp}")
|
| 933 |
+
except Exception as e:
|
| 934 |
+
logger.debug(f"Could not clean {sp}: {e}")
|
| 935 |
+
|
| 936 |
+
if cleaned_count > 0:
|
| 937 |
+
logger.info(f"Pre-read cleanup: cleaned {cleaned_count} status file(s)")
|
| 938 |
+
|
| 939 |
+
# Now read from the primary location
|
| 940 |
status_file = _find_status_file()
|
| 941 |
with open(status_file) as f:
|
| 942 |
data = json.load(f)
|
|
|
|
| 952 |
error_normalized = error.strip().lower()
|
| 953 |
# Check if it's a "null" error (empty, unknown, none, or just whitespace)
|
| 954 |
if error_normalized in ("", "unknown", "none", "null"):
|
| 955 |
+
# This should not happen after pre-read cleanup, but handle defensively
|
| 956 |
+
# Just return the stage without _ERROR suffix
|
| 957 |
+
logger.debug(f"Read stage with 'null' error '{error}' - treating as healthy")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 958 |
return stage
|
| 959 |
else:
|
| 960 |
# Real error detected - append _ERROR to stage
|
|
|
|
| 964 |
return stage
|
| 965 |
except Exception as e:
|
| 966 |
# Log the actual error for debugging
|
|
|
|
|
|
|
| 967 |
logger.warning(f"Failed to read cain_status.json from {status_file if 'status_file' in locals() else 'unknown'}: {e}")
|
| 968 |
logger.debug(f"Exception details: ", exc_info=True)
|
| 969 |
return 'UNKNOWN'
|