错误处理
本文档介绍 Unifiles API 的错误响应格式、常见错误码以及最佳处理实践。
错误响应格式
所有 API 错误都遵循统一的响应格式:
{
"error": {
"code": "INVALID_REQUEST",
"message": "请求参数无效",
"details": {
"field": "file_id",
"reason": "文件ID格式不正确"
},
"request_id": "req_abc123xyz"
}
}
| 字段 |
类型 |
说明 |
code |
string |
错误码,用于程序处理 |
message |
string |
人类可读的错误描述 |
details |
object |
可选,错误的详细信息 |
request_id |
string |
请求ID,用于排查问题 |
HTTP 状态码
| 状态码 |
说明 |
常见场景 |
400 |
Bad Request |
请求参数错误 |
401 |
Unauthorized |
未提供或无效的 API Key |
403 |
Forbidden |
权限不足 |
404 |
Not Found |
资源不存在 |
409 |
Conflict |
资源冲突 |
413 |
Payload Too Large |
请求体过大 |
415 |
Unsupported Media Type |
不支持的文件格式 |
422 |
Unprocessable Entity |
业务逻辑错误 |
429 |
Too Many Requests |
超出速率限制 |
500 |
Internal Server Error |
服务器内部错误 |
503 |
Service Unavailable |
服务暂时不可用 |
错误码参考
认证错误 (401)
| 错误码 |
说明 |
解决方法 |
INVALID_API_KEY |
API Key 无效 |
检查 API Key 是否正确 |
EXPIRED_API_KEY |
API Key 已过期 |
重新生成 API Key |
REVOKED_API_KEY |
API Key 已被撤销 |
联系管理员或创建新 Key |
MISSING_AUTH |
未提供认证信息 |
添加 Authorization 头 |
from unifiles.exceptions import AuthenticationError
try:
client.files.list()
except AuthenticationError as e:
if e.code == "INVALID_API_KEY":
print("API Key 无效,请检查配置")
elif e.code == "EXPIRED_API_KEY":
print("API Key 已过期,请重新生成")
权限错误 (403)
| 错误码 |
说明 |
解决方法 |
INSUFFICIENT_SCOPE |
API Key 权限不足 |
使用具有相应权限的 Key |
RESOURCE_ACCESS_DENIED |
无权访问该资源 |
检查资源所有权 |
QUOTA_EXCEEDED |
配额已用完 |
升级计划或等待重置 |
from unifiles.exceptions import PermissionError
try:
client.files.delete(file_id)
except PermissionError as e:
if e.code == "INSUFFICIENT_SCOPE":
print("当前 API Key 没有删除权限")
elif e.code == "QUOTA_EXCEEDED":
print("存储配额已满,请清理文件或升级计划")
资源错误 (404)
| 错误码 |
说明 |
解决方法 |
FILE_NOT_FOUND |
文件不存在 |
检查文件ID是否正确 |
EXTRACTION_NOT_FOUND |
提取任务不存在 |
检查提取ID |
KNOWLEDGE_BASE_NOT_FOUND |
知识库不存在 |
检查知识库ID |
DOCUMENT_NOT_FOUND |
文档不存在 |
检查文档ID |
WEBHOOK_NOT_FOUND |
Webhook 不存在 |
检查 Webhook ID |
from unifiles.exceptions import NotFoundError
try:
file = client.files.get(file_id)
except NotFoundError as e:
if e.code == "FILE_NOT_FOUND":
print(f"文件 {file_id} 不存在")
请求错误 (400/422)
| 错误码 |
说明 |
解决方法 |
INVALID_REQUEST |
请求格式错误 |
检查请求参数 |
INVALID_FILE_ID |
文件ID格式无效 |
使用正确的ID格式 |
INVALID_PARAMETER |
参数值无效 |
检查参数范围和类型 |
MISSING_PARAMETER |
缺少必需参数 |
补充必需参数 |
INVALID_FILE_TYPE |
不支持的文件类型 |
使用支持的文件格式 |
FILE_TOO_LARGE |
文件过大 |
压缩文件或分片上传 |
EXTRACTION_NOT_COMPLETED |
提取未完成 |
等待提取完成后操作 |
DUPLICATE_DOCUMENT |
文档已存在于知识库 |
先删除或使用更新操作 |
from unifiles.exceptions import ValidationError
try:
kb = client.knowledge_bases.create(
name="", # 空名称
chunking_strategy={"type": "invalid"}
)
except ValidationError as e:
print(f"参数错误: {e.message}")
if e.details:
print(f"问题字段: {e.details.get('field')}")
print(f"原因: {e.details.get('reason')}")
文件处理错误 (422)
| 错误码 |
说明 |
解决方法 |
UNSUPPORTED_FORMAT |
不支持的文件格式 |
检查支持的格式列表 |
CORRUPTED_FILE |
文件已损坏 |
重新上传原始文件 |
ENCRYPTED_FILE |
文件有密码保护 |
移除密码后重试 |
EMPTY_FILE |
文件内容为空 |
检查文件内容 |
OCR_FAILED |
OCR 识别失败 |
尝试不同的提取模式 |
EXTRACTION_TIMEOUT |
提取超时 |
重试或使用更简单的模式 |
from unifiles.exceptions import ProcessingError
try:
extraction = client.extractions.create(file_id=file.id)
extraction.wait()
except ProcessingError as e:
if e.code == "OCR_FAILED":
# 尝试降级到 simple 模式
extraction = client.extractions.create(
file_id=file.id,
mode="simple"
)
elif e.code == "ENCRYPTED_FILE":
print("请移除文件密码后重新上传")
速率限制错误 (429)
| 错误码 |
说明 |
解决方法 |
RATE_LIMIT_EXCEEDED |
请求频率超限 |
等待后重试 |
CONCURRENT_LIMIT_EXCEEDED |
并发请求超限 |
减少并发数 |
from unifiles.exceptions import RateLimitError
import time
try:
result = client.files.upload("document.pdf")
except RateLimitError as e:
wait_time = e.retry_after or 60
print(f"请求过于频繁,{wait_time}秒后重试")
time.sleep(wait_time)
# 重试
result = client.files.upload("document.pdf")
服务器错误 (500/503)
| 错误码 |
说明 |
解决方法 |
INTERNAL_ERROR |
内部服务错误 |
稍后重试 |
SERVICE_UNAVAILABLE |
服务暂时不可用 |
稍后重试 |
DATABASE_ERROR |
数据库错误 |
稍后重试 |
STORAGE_ERROR |
存储服务错误 |
稍后重试 |
from unifiles.exceptions import ServerError
try:
result = client.files.list()
except ServerError as e:
if e.code in ["INTERNAL_ERROR", "SERVICE_UNAVAILABLE"]:
print("服务暂时不可用,请稍后重试")
# 实现重试逻辑
SDK 异常类层次
UnifilesError (基类)
├── AuthenticationError # 401 认证错误
├── PermissionError # 403 权限错误
├── NotFoundError # 404 资源不存在
├── ValidationError # 400/422 验证错误
├── ProcessingError # 422 处理错误
├── RateLimitError # 429 速率限制
├── ServerError # 500/503 服务器错误
└── TimeoutError # 请求超时
使用示例
from unifiles import UnifilesClient
from unifiles.exceptions import (
UnifilesError,
AuthenticationError,
PermissionError,
NotFoundError,
ValidationError,
ProcessingError,
RateLimitError,
ServerError,
TimeoutError
)
client = UnifilesClient(api_key="sk_...")
try:
file = client.files.upload("document.pdf")
extraction = client.extractions.create(file_id=file.id)
extraction.wait()
except AuthenticationError as e:
# 认证问题
print(f"认证失败: {e.message}")
except PermissionError as e:
# 权限问题
print(f"权限不足: {e.message}")
except NotFoundError as e:
# 资源不存在
print(f"资源不存在: {e.message}")
except ValidationError as e:
# 参数验证失败
print(f"参数错误: {e.message}")
if e.details:
print(f"详情: {e.details}")
except ProcessingError as e:
# 文件处理失败
print(f"处理失败: {e.message}")
print(f"错误码: {e.code}")
except RateLimitError as e:
# 超出速率限制
print(f"请求过于频繁,请在 {e.retry_after} 秒后重试")
except ServerError as e:
# 服务器错误
print(f"服务器错误: {e.message}")
print(f"请求ID: {e.request_id}")
except TimeoutError as e:
# 请求超时
print("请求超时,请检查网络或稍后重试")
except UnifilesError as e:
# 其他 Unifiles 错误
print(f"未知错误: {e.message}")
最佳实践
1. 使用结构化错误处理
def handle_file_upload(file_path: str) -> dict:
"""带完整错误处理的文件上传"""
try:
file = client.files.upload(file_path)
return {"success": True, "file_id": file.id}
except ValidationError as e:
return {
"success": False,
"error_type": "validation",
"message": e.message,
"details": e.details
}
except ProcessingError as e:
return {
"success": False,
"error_type": "processing",
"code": e.code,
"message": e.message
}
except RateLimitError as e:
return {
"success": False,
"error_type": "rate_limit",
"retry_after": e.retry_after
}
except UnifilesError as e:
return {
"success": False,
"error_type": "unknown",
"message": e.message,
"request_id": e.request_id
}
2. 实现带重试的操作
import time
from typing import TypeVar, Callable
T = TypeVar('T')
def with_retry(
func: Callable[[], T],
max_attempts: int = 3,
retry_on: tuple = (ServerError, TimeoutError, RateLimitError)
) -> T:
"""通用重试装饰器"""
last_error = None
for attempt in range(max_attempts):
try:
return func()
except retry_on as e:
last_error = e
if isinstance(e, RateLimitError):
wait_time = e.retry_after or 60
else:
wait_time = 2 ** attempt # 指数退避
if attempt < max_attempts - 1:
print(f"尝试 {attempt + 1} 失败,{wait_time}秒后重试...")
time.sleep(wait_time)
raise last_error
# 使用示例
file = with_retry(lambda: client.files.upload("document.pdf"))
3. 记录错误日志
import logging
logger = logging.getLogger("unifiles")
def upload_file_with_logging(file_path: str):
"""带日志记录的文件上传"""
try:
file = client.files.upload(file_path)
logger.info(f"文件上传成功: {file.id}")
return file
except UnifilesError as e:
logger.error(
f"文件上传失败",
extra={
"file_path": file_path,
"error_code": e.code,
"error_message": e.message,
"request_id": e.request_id
}
)
raise
4. 优雅降级
def extract_with_fallback(file_id: str) -> str:
"""带降级策略的内容提取"""
modes = ["advanced", "normal", "simple"]
for mode in modes:
try:
extraction = client.extractions.create(
file_id=file_id,
mode=mode
)
extraction.wait()
if extraction.status == "completed":
return extraction.markdown
except ProcessingError as e:
if e.code in ["OCR_FAILED", "EXTRACTION_TIMEOUT"]:
print(f"{mode}模式失败,尝试降级...")
continue
raise
raise Exception("所有提取模式均失败")
5. 用户友好的错误提示
ERROR_MESSAGES = {
"INVALID_API_KEY": "API密钥无效,请检查您的配置",
"QUOTA_EXCEEDED": "您的存储配额已满,请升级计划或清理文件",
"FILE_TOO_LARGE": "文件大小超过限制(最大100MB)",
"UNSUPPORTED_FORMAT": "不支持的文件格式,请查看支持的格式列表",
"RATE_LIMIT_EXCEEDED": "请求过于频繁,请稍后再试",
"SERVICE_UNAVAILABLE": "服务暂时不可用,请稍后再试"
}
def get_user_friendly_message(error: UnifilesError) -> str:
"""获取用户友好的错误提示"""
return ERROR_MESSAGES.get(
error.code,
f"操作失败: {error.message}"
)
调试技巧
1. 使用 request_id 排查问题
每个错误响应都包含 request_id,联系支持时请提供:
try:
result = client.files.upload("document.pdf")
except UnifilesError as e:
print(f"错误: {e.message}")
print(f"请求ID: {e.request_id}") # 提供给技术支持
2. 启用调试日志
import logging
# 启用 SDK 调试日志
logging.getLogger("unifiles").setLevel(logging.DEBUG)
# 查看完整的请求/响应
client = UnifilesClient(
api_key="sk_...",
debug=True # 启用调试模式
)
3. 检查响应详情
try:
result = client.files.upload("document.pdf")
except UnifilesError as e:
print(f"状态码: {e.status_code}")
print(f"错误码: {e.code}")
print(f"错误信息: {e.message}")
print(f"详情: {e.details}")
print(f"请求ID: {e.request_id}")
下一步