MLflow 异常体系深度解析:从 MlflowException 到 RestException 的源码级指南
【免费下载链接】mlflowThe open source AI engineering platform for agents, LLMs, and ML models. MLflow enables teams of all sizes to debug, evaluate, monitor, and optimize production-quality AI applications while controlling costs and managing access to models and data.项目地址: https://gitcode.com/GitHub_Trending/ml/mlflow
mlflow.exceptions是 MLflow 面向外部操作(Tracking、Model Registry、Gateway、Tracing 等)统一抛出的异常模块。本篇以 mlflow.exceptions.rst 为骨架,结合 mlflow/exceptions.py、mlflow/error_classification.py、mlflow/utils/rest_utils.py 与 tests/test_exceptions.py 源码,完整梳理异常类的签名、error_code 映射表、序列化格式与 REST 链路中的真实调用方式,帮助你写出健壮的错误处理代码并读懂 MLflow 的报错信息。
一、模块概览:一个异常、一套编码、三类用途
mlflow.exceptions的核心设计可以概括为"一个基类 + 一张编码表 + 若干语义子类":
- 一个基类:
MlflowException,所有 MLflow 操作失败的通用异常; - 一张编码表:
ERROR_CODE_TO_HTTP_STATUS,把 protobuf 定义的ErrorCode枚举映射为 HTTP 状态码,同时提供反向映射; - 若干语义子类:包括面向 REST 调用的
RestException、面向 MLflow Project 执行的ExecutionException、面向配置缺失的MissingConfigException、面向非法 URL 的InvalidUrlException,以及 Tracing 相关的异常族。
文档中给出的基类签名为:
mlflow.exceptions.MlflowException(message, error_code=1, **kwargs)其中error_code=1正是 protobuf 枚举中INTERNAL_ERROR的取值。在 databricks.proto 中可以看到完整的枚举定义:INTERNAL_ERROR = 1、TEMPORARILY_UNAVAILABLE = 2、BAD_REQUEST = 4……而 1000 以上的取值是 MLflow 自定义的业务编码,例如INVALID_PARAMETER_VALUE = 1000、ENDPOINT_NOT_FOUND = 1001、INVALID_STATE = 1003、PERMISSION_DENIED = 1004、CUSTOMER_UNAUTHORIZED = 1006、REQUEST_LIMIT_EXCEEDED = 1007、RESOURCE_CONFLICT = 1008、NOT_IMPLEMENTED = 1010、DATA_LOSS = 1011、RESOURCE_ALREADY_EXISTS = 3001、RESOURCE_DOES_NOT_EXIST = 3002。这些常量由mlflow.protos.databricks_pb2导出,业务代码中直接 import 即可。
二、MlflowException:构造参数与字段语义
在 mlflow/exceptions.py 中,MlflowException的实际构造签名比文档标注更丰富:
def __init__( self, message: str, error_code: int = INTERNAL_ERROR, sqlstate: str | None = None, error_class: str | None = None, **kwargs, ):各参数含义如下:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
message | str | 必填 | 错误描述文本,会进入序列化 JSON,也会作为str(exception)的内容 |
error_code | int | INTERNAL_ERROR(1) | 来自databricks_pb2的错误码;非法值会被回退为INTERNAL_ERROR |
sqlstate | str | None(自动推导) | 5 字符 SQLSTATE 编码,用于可靠性看板分类错误 |
error_class | str | None(自动推导) | 描述性错误类别名,如SCHEMA_ENFORCEMENT_FAILED |
**kwargs | dict | 空 | 附加键值对,会并入序列化 JSON 输出 |
构造完成后的实例字段包括error_code(字符串形式的ErrorCode.Name)、message、error_class、sqlstate以及json_kwargs。默认的sqlstate与error_class推导链是:优先使用显式传入值;未传入时先由error_code推导error_class,再由error_class推导sqlstate(见 mlflow/error_classification.py 的注释说明)。
2.1 派生工厂方法 invalid_parameter_value
对于最常见的参数非法场景,模块提供了类方法MlflowException.invalid_parameter_value(message, sqlstate=None, error_class=None, **kwargs),等价于MlflowException(message, error_code=INVALID_PARAMETER_VALUE, ...)。在 tests/test_exceptions.py 中有对应验证:默认自动推导出sqlstate="KAM00"、error_class="INVALID_PARAMETER_VALUE";也支持显式覆盖,例如传入sqlstate="KAM02", error_class="PREDICTION_FUNCTION_FAILED"。
2.2 序列化与 HTTP 状态码
def serialize_as_json(self): exception_dict = {"error_code": self.error_code, "message": self.message} if self.sqlstate is not None: exception_dict["sqlstate"] = self.sqlstate if self.error_class is not None: exception_dict["error_class"] = self.error_class exception_dict.update(self.json_kwargs) return json.dumps(exception_dict) def get_http_status_code(self): return ERROR_CODE_TO_HTTP_STATUS.get(self.error_code, 500)serialize_as_json()输出形如{"error_code": "...", "message": "...", "sqlstate": "...", "error_class": "..."}的 JSON 字符串,**kwargs中的附加字段会合并进去;get_http_status_code()依据ERROR_CODE_TO_HTTP_STATUS映射表返回 HTTP 状态码,未知编码一律回退为 500。
三、error_code 与 HTTP 状态码的完整映射
mlflow/exceptions.py 维护了两张方向相反的表,这是理解 MLflow 报错的关键:
| ErrorCode(protobuf) | HTTP 状态码 | 语义 |
|---|---|---|
INTERNAL_ERROR/INVALID_STATE/DATA_LOSS | 500 | 内部错误 / 非法状态 / 数据丢失 |
NOT_IMPLEMENTED | 501 | 功能未实现 |
TEMPORARILY_UNAVAILABLE | 503 | 临时不可用 |
DEADLINE_EXCEEDED | 504 | 超时 |
REQUEST_LIMIT_EXCEEDED/RESOURCE_EXHAUSTED | 429 | 请求 / 资源超限 |
CANCELLED | 499 | 客户端主动取消 |
ABORTED/RESOURCE_CONFLICT/ALREADY_EXISTS | 409 | 冲突 / 已存在 |
NOT_FOUND/ENDPOINT_NOT_FOUND/RESOURCE_DOES_NOT_EXIST | 404 | 资源不存在 |
PERMISSION_DENIED | 403 | 权限拒绝 |
CUSTOMER_UNAUTHORIZED/UNAUTHENTICATED | 401 | 未认证 / 未授权 |
BAD_REQUEST/RESOURCE_ALREADY_EXISTS/INVALID_PARAMETER_VALUE | 400 | 请求错误 / 参数非法 |
反向表HTTP_STATUS_TO_ERROR_CODE将 HTTP 码映射回 ErrorCode,并额外处理了三种歧义:400 固定为BAD_REQUEST、404 固定为ENDPOINT_NOT_FOUND、500 固定为INTERNAL_ERROR(因为一个 HTTP 码可能对应多个 ErrorCode)。模块级函数get_error_code(http_status)正是基于该反向表把未知 HTTP 状态兜底为INTERNAL_ERROR。这些行为在 tests/test_exceptions.py 中被逐一断言:ENDPOINT_NOT_FOUND -> 404、INVALID_PARAMETER_VALUE -> 400、RESOURCE_ALREADY_EXISTS -> 400,未收录的编码(如IO_ERROR)也稳定回退为 500。
四、错误分类体系:sqlstate 与 error_class 的推导规则
MLflow 近期的版本为异常引入了结构化分类能力,实现在 mlflow/error_classification.py:
- error_class:比 error_code 更细粒度的分类(如
SCHEMA_ENFORCEMENT_FAILED、ATTRIBUTE_NOT_FOUND、MODEL_SERIALIZATION_FAILED、PREDICTION_FUNCTION_FAILED),未显式指定时由 error_code 自动推导; - sqlstate:5 字符编码,供可靠性看板聚合错误,未显式指定时先按 error_class、再按 error_code 推导。
分类命名空间分客户端与服务端两套:客户端错误使用KAM0x/XXM0x,例如KAM00(非法参数)、KAM01(schema 强制失败)、KAM04(属性未找到)、XXM00(客户端内部错误);服务端(CP/server)使用KAMCx/XXMCx,例如KAMC1(权限拒绝)、KAMC2(资源不存在)、KAMC4(非法参数)、XXMC0(内部错误)。二者互不混淆,RestException构造时会根据来源自动选择 CP 映射。
从 tests/test_exceptions.py 可确认推导结果:默认构造MlflowException("test")得到sqlstate="XXM00"、error_class="CLIENT_INTERNAL_ERROR";显式传入sqlstate="KAM01", error_class="SCHEMA_ENFORCEMENT_FAILED"时按显式值输出;而对IO_ERROR这类未收录编码,sqlstate与error_class均为None,序列化 JSON 中也不会出现这两个字段。
使用建议:绝大多数 raise 点无需手动传sqlstate或error_class,二者都会由 error_code 自动推导;只有当 error_code 过粗、无法区分具体失败模式时(例如同一个INVALID_PARAMETER_VALUE既用于 schema 强制失败又用于属性查找失败)才显式传入error_class;sqlstate则始终由 error_class 推导,不建议直接传值。
五、RestException:REST API 非 200 响应的统一异常
RestException(MlflowException)的定位是"REST API 返回非 200 级响应时抛出的异常",构造函数接收服务端返回的 JSON 字典:
class RestException(MlflowException): def __init__(self, json): self.json = json error_code = json.get("error_code") or ErrorCode.Name(INTERNAL_ERROR) message = "{}: {}".format(error_code, json["message"] if "message" in json else "Response: " + str(json)) # 尝试解析 error_code;若为 HTTP 码则经 HTTP_STATUS_TO_ERROR_CODE 转换; # 无法识别的编码记录 warning 并回退到 INTERNAL_ERROR ... # 从响应体保留 sqlstate/error_class,缺失时按 CP 映射推导它的容错能力体现在三个"兜底"路径(均有测试覆盖):
- 缺失/空 error_code:回退为
INTERNAL_ERROR(tests/test_exceptions.py); - error_code 是 HTTP 状态码:如
"403",经HTTP_STATUS_TO_ERROR_CODE转换为PERMISSION_DENIED(tests/test_exceptions.py); - 完全无法识别的编码:记录
logger.warning(提示错误可能发生在到达 MLflow server 之前的代理或认证服务中),并以INTERNAL_ERROR构造(tests/test_exceptions.py)。
此外,RestException通过重写__reduce__返回(RestException, (self.json,))使自己可被 pickle 序列化,便于跨进程传播(tests/test_exceptions.py)。
5.1 REST 链路中的真实调用点
在 mlflow/utils/rest_utils.py 中,http_request_safe()包装http_request()并调用verify_rest_response()校验响应:
- 状态码等于
expected_status(默认 200)时正常返回; - 状态码不符时,若响应体可解析为 JSON 字典,则
raise RestException(json.loads(response.text)),把服务端错误原样封装; - 若响应体不是合法 JSON,则用
get_error_code(response.status_code)推导编码,并显式带上 CP 侧的sqlstate/error_class构造MlflowException,例如"API request to endpoint ... failed with error code 404 != 200"。
因此,客户端捕获到的RestException.error_code、sqlstate、error_class很可能直接来自服务端序列化后的 JSON(见 tests/test_exceptions.py 中"保留服务端 sqlstate、忽略空值"的断言)。理解这一链路,排查"代理/网关返回的 502、504"这类非 MLflow 错误时就能一眼识别 warning 日志的含义。
六、其余异常子类一览
| 异常类 | 触发场景 |
|---|---|
ExecutionException | MLflow Project 执行失败时抛出(见 mlflow/exceptions.py) |
MissingConfigException | 期望的配置文件 / 目录未找到时抛出(mlflow/exceptions.py) |
InvalidUrlException | 因 URL 非法导致 HTTP 请求发送失败时抛出(mlflow/exceptions.py) |
_UnsupportedMultipartUploadException/_UnsupportedMultipartDownloadException | 当前 artifact 仓库不支持分段上传 / 下载,固定以NOT_IMPLEMENTED抛出(mlflow/exceptions.py) |
_UnsupportedPresignedUploadException/_UnsupportedPresignedDownloadException | 当前 artifact 仓库不支持预签名上传 / 下载,固定以NOT_IMPLEMENTED抛出(mlflow/exceptions.py) |
MlflowTracingException | Tracing 逻辑内部错误。由于 Tracing 原则上不应阻塞主执行流,此异常用于区分并妥善处理 Tracing 相关错误(mlflow/exceptions.py) |
MlflowTraceDataException | Trace 数据相关错误,依据NOT_FOUND/INVALID_STATE生成 "Trace data not found / corrupted for request_id=..." 消息(mlflow/exceptions.py) |
MlflowTraceDataNotFound/MlflowTraceDataCorrupted | 分别对应 Trace 数据未找到与数据损坏,是上者的两个便捷子类(mlflow/exceptions.py) |
MlflowTraceArchivalMalformedTrace | Trace 归档序列化发现畸形内容,以INVALID_PARAMETER_VALUE抛出(mlflow/exceptions.py) |
MlflowNotImplementedException | 功能未实现,固定以NOT_IMPLEMENTED抛出,消息默认为空(mlflow/exceptions.py) |
注意,以单下划线开头的_Unsupported*四个类是模块私有实现,不对外承诺 API 稳定性;其余类均可从mlflow.exceptions直接导入使用。
七、在业务代码中的典型用法
7.1 捕获并读取错误信息
import mlflow from mlflow.exceptions import MlflowException, RestException try: mlflow.search_runs(experiment_ids=["not_exist"]) except RestException as e: # e.error_code 为服务端返回的 ErrorCode 字符串,如 "RESOURCE_DOES_NOT_EXIST" # e.sqlstate / e.error_class 为服务端分类(缺失时按 CP 映射推导) print(e.error_code, e.message, e.get_http_status_code()) except MlflowException as e: # 客户端本地操作失败,error_code 默认 "INTERNAL_ERROR" print(e.error_code, e.serialize_as_json())7.2 按状态码做分支处理
from mlflow.exceptions import MlflowException from mlflow.protos.databricks_pb2 import NOT_FOUND, PERMISSION_DENIED, REQUEST_LIMIT_EXCEEDED try: run_operation() except MlflowException as e: if e.error_code == "NOT_FOUND": pass # 资源不存在,执行重建逻辑 elif e.error_code == "PERMISSION_DENIED": pass # 检查凭据与权限 elif e.error_code == "REQUEST_LIMIT_EXCEEDED": pass # 被限流,建议退避重试7.3 抛出规范异常(自定义扩展 / 插件开发)
from mlflow.exceptions import MlflowException, MlflowNotImplementedException from mlflow.protos.databricks_pb2 import RESOURCE_DOES_NOT_EXIST # 带业务错误码 raise MlflowException( "experiment 'x' does not exist", error_code=RESOURCE_DOES_NOT_EXIST, # 可选:显式补充细粒度分类,其余字段自动推导 error_class="RESOURCE_NOT_FOUND", ) # 参数非法快捷方式 raise MlflowException.invalid_parameter_value("batch_size must be positive") # 未实现功能 raise MlflowNotImplementedException("custom endpoint is not supported yet")7.4 安全提示
MlflowException的 message 可能被直接暴露在 HTTP 响应中供客户端调试。若错误文本涉及敏感信息,源码注释明确建议改用普通Exception(见 mlflow/exceptions.py),避免敏感信息随 REST 响应外泄。
八、写在最后:排查错误的三个切入点
- 看 error_code:它决定 HTTP 状态码(见第三节映射表),先确认是 4xx(客户端问题)还是 5xx(服务端问题);
- 看 error_class / sqlstate:若出现
KAM01/SCHEMA_ENFORCEMENT_FAILED这类细粒度编码,说明错误发生在具体业务校验(如模型 schema 强制)阶段,比 error_code 更能定位根因; - 看 RestException 的来源:当遇到无法识别的 error_code 时,warning 日志提示请求可能在到达 MLflow server 前就被代理或认证服务拦截,此时应检查中间链路而非 MLflow 本身。
以上结论均可对照 mlflow/exceptions.py、mlflow/error_classification.py、mlflow/utils/rest_utils.py 与 tests/test_exceptions.py 复现验证,API 文档原文见 mlflow.exceptions.rst。
【免费下载链接】mlflowThe open source AI engineering platform for agents, LLMs, and ML models. MLflow enables teams of all sizes to debug, evaluate, monitor, and optimize production-quality AI applications while controlling costs and managing access to models and data.项目地址: https://gitcode.com/GitHub_Trending/ml/mlflow
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考