Claude Code commited on
Commit
93d76e5
·
1 Parent(s): c8b024d

Claude Code: Investigate the file to identify the cause of the "unknown" run

Browse files
.openclaw/agents/__pycache__/brain_minimal.cpython-311.pyc CHANGED
Binary files a/.openclaw/agents/__pycache__/brain_minimal.cpython-311.pyc and b/.openclaw/agents/__pycache__/brain_minimal.cpython-311.pyc differ
 
.openclaw/agents/__pycache__/rbac.cpython-311.pyc CHANGED
Binary files a/.openclaw/agents/__pycache__/rbac.cpython-311.pyc and b/.openclaw/agents/__pycache__/rbac.cpython-311.pyc differ
 
.openclaw/agents/brain_minimal.py CHANGED
@@ -666,10 +666,12 @@ class BrainMinimal:
666
  return {
667
  "success": True,
668
  "status": "healthy",
 
669
  "state": self.brain_state.value,
670
  "agent": self.agent_name,
671
  "role": self.current_role.value if self.current_role else "unknown",
672
- "tools_count": len(self.get_allowed_tools())
 
673
  }
674
 
675
  def _get_status(self) -> Dict[str, Any]:
 
666
  return {
667
  "success": True,
668
  "status": "healthy",
669
+ "stage": "RUNNING",
670
  "state": self.brain_state.value,
671
  "agent": self.agent_name,
672
  "role": self.current_role.value if self.current_role else "unknown",
673
+ "tools_count": len(self.get_allowed_tools()),
674
+ "error": None
675
  }
676
 
677
  def _get_status(self) -> Dict[str, Any]:
.openclaw/agents/cain_status.json CHANGED
@@ -1,5 +1,5 @@
1
  {
2
  "current_state": "idle",
3
- "last_updated": "2026-03-14T19:10:02.929644+00:00",
4
  "agent": "cain"
5
  }
 
1
  {
2
  "current_state": "idle",
3
+ "last_updated": "2026-03-14T19:17:42.455943+00:00",
4
  "agent": "cain"
5
  }
.openclaw/agents/logs/session-archive.jsonl CHANGED
@@ -19,3 +19,6 @@
19
  {"type": "state_change", "state": "processing", "timestamp": "2026-03-14T19:10:02.927551+00:00", "agent": "cain"}
20
  {"type": "state_change", "state": "success", "timestamp": "2026-03-14T19:10:02.928572+00:00", "agent": "cain"}
21
  {"type": "state_change", "state": "idle", "timestamp": "2026-03-14T19:10:02.929525+00:00", "agent": "cain"}
 
 
 
 
19
  {"type": "state_change", "state": "processing", "timestamp": "2026-03-14T19:10:02.927551+00:00", "agent": "cain"}
20
  {"type": "state_change", "state": "success", "timestamp": "2026-03-14T19:10:02.928572+00:00", "agent": "cain"}
21
  {"type": "state_change", "state": "idle", "timestamp": "2026-03-14T19:10:02.929525+00:00", "agent": "cain"}
22
+ {"type": "state_change", "state": "processing", "timestamp": "2026-03-14T19:17:35.121684+00:00", "agent": "cain"}
23
+ {"type": "state_change", "state": "success", "timestamp": "2026-03-14T19:17:35.122941+00:00", "agent": "cain"}
24
+ {"type": "state_change", "state": "idle", "timestamp": "2026-03-14T19:17:35.124108+00:00", "agent": "cain"}
__pycache__/app.cpython-311.pyc CHANGED
Binary files a/__pycache__/app.cpython-311.pyc and b/__pycache__/app.cpython-311.pyc differ
 
app.py CHANGED
@@ -9,6 +9,8 @@ import os
9
  import sys
10
  import json
11
  import asyncio
 
 
12
  from typing import Dict, Any, List, Optional
13
  from datetime import datetime
14
  from pathlib import Path
@@ -18,6 +20,21 @@ from fastapi.responses import JSONResponse
18
  from pydantic import BaseModel, Field
19
  import uvicorn
20
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  # Paths
22
  WORKSPACE_DIR = Path("/tmp/claude-workspace")
23
  OPENCLAW_HOME = Path.home() / ".openclaw"
@@ -36,16 +53,20 @@ sys.path.insert(0, str(BASE_DIR))
36
  try:
37
  from brain_minimal import get_brain, BrainState
38
  BRAIN_AVAILABLE = True
39
- except ImportError:
 
40
  BRAIN_AVAILABLE = False
41
- print("[API] Warning: brain_minimal not available")
 
42
 
43
  try:
44
  from health_monitor import get_health_monitor, LogLevel, HealthStatus
45
  HEALTH_MONITOR_AVAILABLE = True
46
- except ImportError:
 
47
  HEALTH_MONITOR_AVAILABLE = False
48
- print("[API] Warning: health_monitor not available")
 
49
 
50
  # FastAPI app
51
  app = FastAPI(
@@ -54,6 +75,41 @@ app = FastAPI(
54
  version="1.0.0"
55
  )
56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  # ========== Request/Response Models ==========
58
 
59
  class ChatMessage(BaseModel):
@@ -101,6 +157,36 @@ class HealthResponse(BaseModel):
101
  _brain_instance = None
102
  _start_time = datetime.utcnow()
103
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
  def get_brain_instance():
105
  """Get or create brain instance"""
106
  global _brain_instance
@@ -158,7 +244,12 @@ async def get_agents_status():
158
  brain = get_brain_instance()
159
 
160
  if brain is None:
161
- raise HTTPException(status_code=503, detail="Brain not available")
 
 
 
 
 
162
 
163
  try:
164
  info = brain.get_info()
@@ -178,7 +269,12 @@ async def get_agents_status():
178
  timestamp=datetime.utcnow().isoformat() + "Z"
179
  )
180
  except Exception as e:
181
- raise HTTPException(status_code=500, detail=f"Error getting agent status: {str(e)}")
 
 
 
 
 
182
 
183
  @app.post("/api/chat", response_model=ChatResponse, tags=["Chat"])
184
  async def chat(message: ChatMessage, background_tasks: BackgroundTasks):
@@ -240,9 +336,10 @@ async def get_health():
240
  health_result = brain.execute_tool("health_check")
241
  components["brain"] = health_result
242
  except Exception as e:
243
- components["brain"] = {"status": "error", "error": str(e)}
 
244
  else:
245
- components["brain"] = {"status": "unavailable"}
246
 
247
  # Health monitor components
248
  if monitor:
@@ -260,18 +357,34 @@ async def get_health():
260
  }
261
  }
262
  except Exception as e:
263
- components["health_monitor"] = {"status": "error", "error": str(e)}
 
 
 
264
 
265
  # Determine overall status
266
  if brain:
267
- brain_health = brain.execute_tool("health_check")
268
- overall_status = brain_health.get("status", "unknown")
269
- stage = brain_health.get("stage", "UNKNOWN")
270
- agent = brain_health.get("agent", "cain")
 
 
 
 
 
 
 
 
 
 
 
 
271
  else:
272
  overall_status = "degraded"
273
  stage = "BRAIN_UNAVAILABLE"
274
  agent = "cain"
 
275
 
276
  return HealthResponse(
277
  status=overall_status,
@@ -517,10 +630,13 @@ try:
517
  app = gr.mount_gradio_app(app, gradio_app, path="/gradio")
518
 
519
  print("[API] Gradio dashboard mounted at /gradio")
520
- except ImportError:
521
- print("[API] Warning: Gradio not available, dashboard not mounted")
 
 
522
  except Exception as e:
523
  print(f"[API] Warning: Could not mount Gradio dashboard: {e}")
 
524
 
525
  # ========== Main Entry Point ==========
526
 
 
9
  import sys
10
  import json
11
  import asyncio
12
+ import traceback
13
+ import logging
14
  from typing import Dict, Any, List, Optional
15
  from datetime import datetime
16
  from pathlib import Path
 
20
  from pydantic import BaseModel, Field
21
  import uvicorn
22
 
23
+ # Configure logging
24
+ LOG_DIR = Path.home() / ".openclaw" / "logs"
25
+ LOG_DIR.mkdir(parents=True, exist_ok=True)
26
+ LOG_FILE = LOG_DIR / "api_errors.log"
27
+
28
+ logging.basicConfig(
29
+ level=logging.INFO,
30
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
31
+ handlers=[
32
+ logging.FileHandler(LOG_FILE),
33
+ logging.StreamHandler(sys.stdout)
34
+ ]
35
+ )
36
+ logger = logging.getLogger("cain_api")
37
+
38
  # Paths
39
  WORKSPACE_DIR = Path("/tmp/claude-workspace")
40
  OPENCLAW_HOME = Path.home() / ".openclaw"
 
53
  try:
54
  from brain_minimal import get_brain, BrainState
55
  BRAIN_AVAILABLE = True
56
+ logger.info("brain_minimal module loaded successfully")
57
+ except ImportError as e:
58
  BRAIN_AVAILABLE = False
59
+ logger.error(f"Failed to import brain_minimal: {e}\n{traceback.format_exc()}")
60
+ print(f"[API] Warning: brain_minimal not available - {e}")
61
 
62
  try:
63
  from health_monitor import get_health_monitor, LogLevel, HealthStatus
64
  HEALTH_MONITOR_AVAILABLE = True
65
+ logger.info("health_monitor module loaded successfully")
66
+ except ImportError as e:
67
  HEALTH_MONITOR_AVAILABLE = False
68
+ logger.error(f"Failed to import health_monitor: {e}\n{traceback.format_exc()}")
69
+ print(f"[API] Warning: health_monitor not available - {e}")
70
 
71
  # FastAPI app
72
  app = FastAPI(
 
75
  version="1.0.0"
76
  )
77
 
78
+
79
+ # ========== Global Exception Handler ==========
80
+
81
+ @app.exception_handler(Exception)
82
+ async def global_exception_handler(request, exc):
83
+ """
84
+ Global exception handler that logs full stack traces for all errors.
85
+ This ensures no error is silently swallowed as "unknown".
86
+ """
87
+ # Get full traceback
88
+ tb_str = traceback.format_exc()
89
+ error_type = type(exc).__name__
90
+ error_msg = str(exc)
91
+
92
+ # Log the full error with traceback
93
+ logger.error(
94
+ f"Unhandled exception: {error_type}: {error_msg}\n"
95
+ f"Path: {request.url.path}\n"
96
+ f"Method: {request.method}\n"
97
+ f"Traceback:\n{tb_str}"
98
+ )
99
+
100
+ # Return detailed error response
101
+ return JSONResponse(
102
+ status_code=500,
103
+ content={
104
+ "error": error_msg,
105
+ "error_type": error_type,
106
+ "traceback": tb_str,
107
+ "path": str(request.url.path),
108
+ "method": request.method,
109
+ "timestamp": datetime.utcnow().isoformat() + "Z"
110
+ }
111
+ )
112
+
113
  # ========== Request/Response Models ==========
114
 
115
  class ChatMessage(BaseModel):
 
157
  _brain_instance = None
158
  _start_time = datetime.utcnow()
159
 
160
+ # ========== Startup Event ==========
161
+
162
+ @app.on_event("startup")
163
+ async def startup_event():
164
+ """Log startup information and verify components"""
165
+ logger.info("=" * 50)
166
+ logger.info("Cain API Starting")
167
+ logger.info(f"Start time: {_start_time.isoformat()}")
168
+ logger.info(f"Brain available: {BRAIN_AVAILABLE}")
169
+ logger.info(f"Health monitor available: {HEALTH_MONITOR_AVAILABLE}")
170
+ logger.info(f"Working directory: {os.getcwd()}")
171
+ logger.info(f"Python version: {sys.version}")
172
+ logger.info(f"API log file: {LOG_FILE}")
173
+ logger.info("=" * 50)
174
+
175
+ # Test brain initialization
176
+ if BRAIN_AVAILABLE:
177
+ try:
178
+ brain = get_brain_instance()
179
+ if brain:
180
+ logger.info(f"Brain initialized: agent={brain.agent_name}, role={brain.current_role}")
181
+ health = brain.execute_tool("health_check")
182
+ logger.info(f"Initial health check: {health}")
183
+ else:
184
+ logger.warning("Brain instance is None after initialization")
185
+ except Exception as e:
186
+ logger.error(f"Brain initialization test failed: {e}\n{traceback.format_exc()}")
187
+ else:
188
+ logger.warning("BRAIN_AVAILABLE=False - brain features will be limited")
189
+
190
  def get_brain_instance():
191
  """Get or create brain instance"""
192
  global _brain_instance
 
244
  brain = get_brain_instance()
245
 
246
  if brain is None:
247
+ logger.error("Brain not available for /api/agents")
248
+ raise HTTPException(
249
+ status_code=503,
250
+ detail="Brain not available",
251
+ headers={"X-Error-Timestamp": datetime.utcnow().isoformat() + "Z"}
252
+ )
253
 
254
  try:
255
  info = brain.get_info()
 
269
  timestamp=datetime.utcnow().isoformat() + "Z"
270
  )
271
  except Exception as e:
272
+ logger.error(f"Error getting agent status: {e}\n{traceback.format_exc()}")
273
+ raise HTTPException(
274
+ status_code=500,
275
+ detail=f"Error getting agent status: {str(e)}",
276
+ headers={"X-Error-Type": type(e).__name__}
277
+ )
278
 
279
  @app.post("/api/chat", response_model=ChatResponse, tags=["Chat"])
280
  async def chat(message: ChatMessage, background_tasks: BackgroundTasks):
 
336
  health_result = brain.execute_tool("health_check")
337
  components["brain"] = health_result
338
  except Exception as e:
339
+ logger.error(f"Brain health check failed: {e}\n{traceback.format_exc()}")
340
+ components["brain"] = {"status": "error", "error": str(e), "traceback": traceback.format_exc()}
341
  else:
342
+ components["brain"] = {"status": "unavailable", "error": "Brain not initialized"}
343
 
344
  # Health monitor components
345
  if monitor:
 
357
  }
358
  }
359
  except Exception as e:
360
+ logger.error(f"Health monitor check failed: {e}\n{traceback.format_exc()}")
361
+ components["health_monitor"] = {"status": "error", "error": str(e), "traceback": traceback.format_exc()}
362
+ else:
363
+ components["health_monitor"] = {"status": "unavailable", "error": "Health monitor not initialized"}
364
 
365
  # Determine overall status
366
  if brain:
367
+ try:
368
+ brain_health = brain.execute_tool("health_check")
369
+ overall_status = brain_health.get("status", "unknown")
370
+ stage = brain_health.get("stage", "UNKNOWN")
371
+ agent = brain_health.get("agent", "cain")
372
+ error = brain_health.get("error", None)
373
+
374
+ # Log if there's an error but status is healthy
375
+ if error:
376
+ logger.warning(f"Health check returned with error: {error}")
377
+ except Exception as e:
378
+ logger.error(f"Failed to get brain health: {e}\n{traceback.format_exc()}")
379
+ overall_status = "error"
380
+ stage = "ERROR"
381
+ agent = "cain"
382
+ error = str(e)
383
  else:
384
  overall_status = "degraded"
385
  stage = "BRAIN_UNAVAILABLE"
386
  agent = "cain"
387
+ error = "Brain not initialized"
388
 
389
  return HealthResponse(
390
  status=overall_status,
 
630
  app = gr.mount_gradio_app(app, gradio_app, path="/gradio")
631
 
632
  print("[API] Gradio dashboard mounted at /gradio")
633
+ logger.info("Gradio dashboard mounted successfully at /gradio")
634
+ except ImportError as e:
635
+ print(f"[API] Warning: Gradio not available, dashboard not mounted - {e}")
636
+ logger.error(f"Failed to import gradio: {e}\n{traceback.format_exc()}")
637
  except Exception as e:
638
  print(f"[API] Warning: Could not mount Gradio dashboard: {e}")
639
+ logger.error(f"Failed to mount Gradio dashboard: {e}\n{traceback.format_exc()}")
640
 
641
  # ========== Main Entry Point ==========
642