OpenHands 源码解析 · 第 15 讲
OpenHands 源码解析系列 第 15 讲:错误处理与日志系统 基于 OpenHands 源码 · 2026-08-11 一、为什么错误处理如此关键 OpenHands 是一个 AI 软件工程平台 ,它管理 Docker/K8s 沙箱、处理 LLM API 调用、操作用户代码仓库。任何一个环节出错都可能造成数据丢失或安全泄露。因此它的错误处理和日志系统不只是一个"nice-to-have",而是 架构的防护骨架 。 本讲深入三个核心模块: 🔹 异常层次体系 ( errors.py )— 5 个自定义异常类的职责划分 🔹 日志框架 ( logger.py ,748 行)— 着色输出、敏感数据脱敏、JSON 结构化日志 🔹 全局异常处理 ( app.py )— FastAPI exception handler + sys.excepthook 兜底 二、异常层次体系:errors.py(62 行) OpenHands 的异常体系继承自 FastAPI 的 HTTPException ,形成了清晰的五层继承链: 异常继承图 HTTPException (FastAPI) └── OpenHandsError ← 默认 500 Internal Server Error ├── AuthError ← 401 Unauthorized (认证失败) ├── PermissionsError ← 403 Forbidden (权限不足) ├── SandboxError ← 500 (沙箱操作失败) └── SandboxDeleteRetryError ← 503 (删除未完成,可重试) 看源码定义: 📄 openhands/app_server/errors.py (第 1-45 行) from typing import Any from fastapi import HTTPException, status class OpenHandsError(HTTPException): """General Error""" def __init__( self, detail: Any = None, headers: dict[str, str] | None = None, status_code: int = status.HTTP_500_INTERNAL_SERVER_ERROR, ): super().__init__(status_code=status_code, detail=detail, headers=headers) class AuthError(OpenHandsError): """Error in authentication.""" def __init__( self, detail: Any = None, headers: dict[str, str] | None = None, status_code: int = status.HTTP_401_UNAUTHORIZED, ): super().__init__(status_code=status_code, detail=detail, headers=headers) class PermissionsError(OpenHandsError): """Error in permissions.""" def __init__( self, detail: Any = None, headers: dict[str, str] | None = None, status_code: int = status.HTTP_403_FORBIDDEN, ): super().__init__(status_code=status_code, detail=detail, headers=headers) class SandboxError(OpenHandsError): """Error in Sandbox.""" 设计要点: 🔹 每个子类覆盖 status_code 默认值,调用方无需每次指定 HTTP 状态码 🔹 SandboxDeleteRetryError 使用 503 而非 404,让客户端区分"资源不存在"和"暂时不可…