首页 产品 为什么选 OCTO 解决方案 文档 关于
文档中心 / API 与集成 / 错误码与排障
← 返回文档中心

错误码与排障

OCTO 文档中心 · API 与集成

  • Octo产品管家Q13源码级回答(2026-09-22)
  • octo-server源码:pkg/errcode/bot_api.go、pkg/httperr/respond.go、modules/bot_api/auth.go、pkg/cardmsg/、modules/bot_api/ratelimit.go、modules/bot_api/file.go

Bot API 错误码与排障 v1.0

本文档基于octo-server源码核查(`pkg/errcode/bot_api.go`、`pkg/httperr/respond.go`等),供Bot/Agent集成开发者参考。

所有错误码和限制均为源码级确认。标注⚠️的为待复核/待Owner补充项。

租户隔离唯一单元是Space,无organization概念。


一、错误响应统一格式

1.1 标准错误结构【✅源码确认 `pkg/httperr/respond.go`】

{
  "code": "err.server.bot_api.request_invalid",
  "message": "Request body is invalid JSON",
  "error": {
    "http_status": 400
  }
}
  • code:错误码字符串,格式为err.server.bot_api.<具体错误>,用于程序化判断
  • message:人类可读的错误描述
  • error.http_status:HTTP状态码

1.2 成功响应


二、完整错误码对照表

2.1 400 Bad Request 系列

code 触发场景 排查建议
request_invalid 请求体JSON格式错误(BindJSON失败)、缺少必填字段、字段类型错误 检查Content-Type是否为application/json,JSON格式是否合法,必填字段是否都传了
limit_exceeded 批量操作超限(members>200 或 message_ids>100) 分批请求,members≤200,message_ids≤100
content_too_large 消息内容超过字段上限 各字段具体上限【✅源码确认 A-4补证 2026-09-22】:群MD/Thread MD默认 10240字节(group.GetGroupMdMaxSize(),env TS_GROUPMDMAXSIZE可调);语音转写上下文 10000字符(rune计);OBO persona_prompt 4096字节(oboPersonaPromptMaxBytes)。错误响应的Details携带field/max_size具体值
file_too_large 上传文件超过大小限制 默认上限100MB,硬顶512MB(system_setting: file.max_size_kb可配置)
file_type_unsupported 文件类型不在白名单、命中扩展名黑名单、魔数(magic number)不匹配 检查文件扩展名和实际文件格式,空扩展名会被拒绝
member_not_human 尝试将非人类用户(如Bot)添加为普通群成员 检查成员UID是否为真实用户
# 1. 未设置Content-Type
curl -X POST http:///v1/bot/messages/send \
  -H "Authorization: Bearer " \
  # ❌ 缺少 -H "Content-Type: application/json"

# 2. JSON语法错误(逗号、引号等)
# 3. 字段名拼写错误(如recv_id写成recvId)
# 4. 必填字段为空字符串

2.2 401 Unauthorized【✅源码确认 `modules/bot_api/auth.go`】

code 触发场景 排查建议
ErrBotAPIAuthFailed 鉴权失败(单一反枚举码) 见下方详细说明
请求进入
  ↓
从 Authorization: Bearer  提取token
  ↓
根据token前缀路由:
  ├─ app_ 前缀 → App Bot鉴权
  ├─ bf_ 前缀 / legacy格式 → User Bot鉴权
  └─ 无前缀/无法识别 → 401
  ↓
查库验证token有效性
  ├─ 有效 → 注入botInfo到context,继续处理
  └─ 无效/过期 → 401 ErrBotAPIAuthFailed
# 1. 确认header格式正确
curl -s -H "Authorization: Bearer " \
  http:///v1/bot/info

# 2. 确认token前缀匹配Bot类型
# App Bot token: app_xxxxxxx
# User Bot token: bf_xxxxxxx (或legacy格式)

# 3. 确认token未过期、未被撤销

2.3 403 Forbidden 系列

code 触发场景 解决方案
not_group_member Bot不是该群组成员 通过API或管理后台将Bot加入群组
not_group_admin 需要群组管理员权限的操作(踢人、改群信息、解散等) 将Bot设为群组管理员
group_disbanded 目标群组已解散 使用有效的群组ID
not_space_member Bot/用户不在目标Space中 将Bot/用户添加到对应Space
app_bot_dm_only App Bot尝试在群组中发消息(仅支持私信场景) 使用支持群组的Bot类型,或改用私信
not_friend 尝试给非好友发私信(非好友关系限制场景) 先添加好友关系
message_edit_forbidden 无权限编辑该消息(非消息发送者/超出编辑时限) 只能编辑自己发送的消息,注意编辑时限
bot_unavailable App Bot未发布(草稿状态) 在管理后台/插件市场发布Bot后使用

2.4 404 Not Found 系列

code 触发场景 排查建议
group_not_found 群组ID不存在 确认groupID正确,群组未被删除
message_not_found 消息ID不存在 确认messageID正确,消息未被撤回/删除
user_not_found 用户UID不存在 确认userID正确,用户在当前Space中

2.5 413 Payload Too Large

code 触发场景 排查建议
payload_too_large 请求体过大(语音转写代理等场景) 减小请求体大小,注意Nginx层也可能有限制

2.6 429 Too Many Requests【✅源码确认 `modules/bot_api/ratelimit.go`】

HTTP/1.1 429 Too Many Requests
Retry-After: 5
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Scope: business

2.7 500 Internal Server Error

code 触发场景 排查建议
im_token_failed 等 服务内部错误(IM token获取失败等) 响应消息脱敏,服务端日志有详细错误信息,联系运维查看服务端日志
# 查看服务端500相关日志
docker compose logs octo-server | grep -iE "error|panic|im_token"
# K8s: kubectl logs -n octo  | grep -iE "error|panic"

三、卡片消息(type=17)发送失败排障【✅源码确认 `pkg/cardmsg/`】

3.1 卡片硬限制清单

限制项 阈值 超限时错误 说明
整包大小 512 KiB ErrCardTooLarge 卡片JSON序列化后的总大小
节点数 ≤ 200 ErrCardTooManyNodes 卡片中所有元素节点总数
嵌套深度 ≤ 16 (校验失败) 元素嵌套层数上限
card_version 必须 = "1.5" ErrCardProfileUnsupported 版本不匹配直接拒绝
元素/动作白名单 必须在白名单内 (校验失败) 不支持的元素类型
URL格式 必须合法 (校验失败) URL格式校验

3.2 卡片排障步骤

# 1. 检查卡片JSON大小
cat card.json | wc -c  # 单位字节,512KiB = 524288字节

# 2. 检查card_version
grep card_version card.json
# 必须是 "card_version": "1.5"

# 3. 简化卡片测试
# 先用最简单的卡片模板测试连通性,逐步添加元素定位问题

3.3 最简测试卡片

{
  "type": 17,
  "card_version": "1.5",
  "version": "1.5",
  "body": {
    "elements": [
      {
        "type": "text",
        "text": "测试卡片",
        "color": "#333333"
      }
    ]
  }
}

3.4 注意事项

  • 无独立校验端点:卡片校验在消息发送入口内联执行,不提供单独的validate接口
  • 建议在开发环境先发送测试消息验证卡片格式
  • 卡片渲染兼容性需在各端(Web/iOS/Android)实际测试

四、限流机制与重试策略【✅源码确认】

4.1 限流架构

维度 说明
算法 令牌桶(Token Bucket),Redis+Lua原子实现
隔离维度 按robotID隔离(一个Bot被限流不影响其他Bot)
限流类别 business(业务请求)/ heartbeat(心跳/长轮询)/ register(注册)

4.2 默认限流参数【✅源码确认 `modules/bot_api/ratelimit.go` + `modules/common/system_settings.go:1382-1400`,2026-09-22 A-2补证】

类别 默认RPS 默认Burst 说明
business 20.0 200 单Bot业务端点令牌桶
heartbeat 1.0 10 下界硬约束:心跳key TTL 60s,低于此一次限流就断联
register 0.5 10 自愈链路最后一环,未鉴权写入口
类别 默认RPS 默认Burst env
register IP 100 500 OCTO_BOT_RATELIMIT_REGISTER_IP_RPS/BURST
heartbeat IP 500 1500 OCTO_BOT_RATELIMIT_HEARTBEAT_IP_RPS/BURST
batch users IP 20 40 OCTO_BOT_RATELIMIT_USERS_BATCH_IP_RPS/BURST

per-bot层参数env:`OCTO_BOT_RATELIMIT_{BUSINESS,HEARTBEAT,REGISTER}_RPS/BURST`;TTL由Lua从参数推ceil(burst/rps×2)。

⚠️ 限流参数可通过环境变量热调整,不需要重启服务。

4.3 429响应头

头 含义
Retry-After 建议等待秒数
X-RateLimit-Limit 当前窗口的请求限额
X-RateLimit-Remaining 当前窗口剩余配额
X-RateLimit-Scope 限流类别(business/heartbeat/register)

4.4 推荐重试策略

import time
import requests

def bot_api_call(url, headers, data, max_retries=3):
    for attempt in range(max_retries):
        resp = requests.post(url, headers=headers, json=data)
        
        if resp.status_code == 200:
            return resp.json()
        
        if resp.status_code == 429:
            retry_after = int(resp.headers.get('Retry-After', 5))
            # 指数退避: retry_after * 2^attempt
            wait = retry_after * (2 ** attempt)
            print(f"限流,等待{wait}秒后重试...")
            time.sleep(wait)
            continue
        
        # 其他错误直接抛出
        resp.raise_for_status()
    
    raise Exception("重试次数耗尽")

4.5 避免限流的最佳实践

  1. 使用批量接口:`POST /v1/bot/users/batch`支持批量操作(members≤200, message_ids≤100),避免逐条发送
  2. 控制并发:限制并发请求数,不要开过多协程/线程
  3. 监控限流头:根据`X-RateLimit-Remaining`动态调整发送速率
  4. 错峰发送:避免在短时间内集中发送大量消息
  5. 消息合并:将多条短消息合并为一条结构化消息(如使用卡片)

五、长轮询事件接口排障【✅源码确认】

5.1 长轮询事件保留窗口TTL【✅源码确认 A-1补证 2026-09-22】

  • 事件队列(ZSet)TTL = Robot.MessageExpire,默认 7天(octo-lib config.go:439 time.Hour*24*7;tsdd.yaml注释示例messageExpire: 7d)
  • 事件写入后对整个队列key执行 EXPIRE(key, MessageExpire)(robot/api.go:296)——即队列TTL随每次新事件写入刷新
  • 门铃key TTL = BellTTL = 5分钟(pkg/botevent/bell.go:38,只需活过最长30s hold)
  • 长轮询 hold 上限 = maxEventWaitSeconds = 30s(events_wait.go:86)

5.2 接口信息

项目 值
方法 POST(不是GET)
路径 /v1/bot/events
断点参数 event_id(int64,exclusive,单调递增)
limit参数 默认20,上限100
wait参数 长轮询等待秒数,服务端clamp到[0, 30]秒

5.3 常见问题

问题 原因 解决方案
立即返回空数组 有backlog时"先读后等",不进入hold 使用返回的最新event_id继续轮询
GET方法返回404 接口只支持POST 改用POST
重复收到同一事件 event_id使用inclusive而非exclusive event_id传上次收到的最后一个事件ID(exclusive)
轮询断开后丢失事件 断点续传窗口有限 ⚠️事件保留TTL待Owner补充,建议断线后尽快重连
wait参数设置>30s无效果 服务端clamp到30s 客户端设置wait≤30,建议10秒

5.4 正确的长轮询实现

event_id = 0

while True:
    try:
        resp = requests.post(
            f"{host}/v1/bot/events",
            headers={"Authorization": f"Bearer {token}"},
            json={"event_id": event_id, "limit": 20, "wait": 10},
            timeout=15  # 客户端硬超时 > wait时间
        )
        events = resp.json()
        for event in events.get("events", []):
            process_event(event)
            event_id = event["event_id"]  # 更新断点
    except requests.Timeout:
        continue  # 超时直接重试
    except Exception as e:
        time.sleep(2)  # 出错后等2秒重试

六、媒体上传排障【✅源码确认 `modules/bot_api/file.go`】

6.1 上传路径【✅源码确认 bot_api.go:447-451 路由注册,A-5补证 2026-09-22】

路径 方法 用途 适用场景
/v1/bot/file/upload(另有别名/v1/bot/upload) POST 服务端中转直传 小文件、简单集成
/v1/bot/upload/credentials GET 获取STS临时密钥,客户端直传COS 大文件、高并发
/v1/bot/upload/presigned GET 预签名URL直接上传 与credentials二选一
下载/v1/bot/file/download/*path GET 302跳转到presigned URL 文件下载

6.2 ✅端点差异已解决(A-5补证 2026-09-22)

已确认:源码中两个端点同时存在(`bot_api.go:450-451`):`GET /v1/bot/upload/credentials`(STS临时密钥)与 `GET /v1/bot/upload/presigned`(预签名URL)均已注册,不是命名冲突,是两条不同的上传路径。之前"文档写presigned、代码是credentials"的差异是因为两者都真实存在。集成时任选其一即可,建议优先credentials(STS)。

6.3 上传限制【✅源码确认 `modules/file/policy.go` + `const.go:183-239`,A-3补证 2026-09-22】

限制项 值 说明
文件大小 默认100MB,硬顶512MB 由system_setting file.max_size_kb控制,MaxUploadSize()是全部7个检查点唯一真源
扩展名 白名单约70种+黑名单31种双门 空扩展名直接拒绝;大小写归一化
魔数校验 有 文件实际内容必须匹配扩展名类型
  • 图片:.jpg/.jpeg/.png/.gif/.bmp/.webp/.ico/.heic/.heif/.tiff/.tif
  • 文档:.pdf/.doc/.docx/.xls/.xlsx/.ppt/.pptx/.txt/.csv/.rtf/.odt/.ods/.odp/.md/.html/.htm
  • iWork:.key/.numbers/.pages;电子书:.epub/.mobi
  • 纯文本/数据:.toml/.ini/.log/.tsv/.ndjson;字幕:.srt/.vtt/.ass
  • 音频:.mp3/.wav/.aac/.flac/.ogg/.wma/.m4a/.amr/.opus/.aiff
  • 视频:.mp4/.avi/.mov/.wmv/.flv/.mkv/.webm/.m4v
  • 压缩包:.zip/.rar/.7z/.tar/.gz/.bz2/.xz
  • 其他:.json/.xml/.yaml/.yml;安装包:.dmg/.pkg/.deb/.rpm/.appimage

部署可通过 system_setting / env(`DM_FILE_EXTRA_ALLOWED`/`DM_FILE_EXTRA_BLOCKED`)追加白/黑名单:allowed = (base ∪ extra) − blocked,黑名单永远压过放开。客户端可从 `/v1/common/appconfig` 拉当前生效白名单(仅下发allowed不下发blocked,防探测)。

6.4 上传常见错误

错误 原因 解决方案
file_too_large (400) 文件超过大小限制 压缩文件或调整file.max_size_kb配置
file_type_unsupported (400) 扩展名不支持或魔数不匹配 确认文件格式正确、扩展名匹配
上传到Nginx返回413 Nginx client_max_body_size限制 在Nginx配置中增大client_max_body_size
STS凭证获取失败 MinIO/COS配置问题 检查对象存储配置和密钥

七、鉴权流程详解【✅源码确认 `modules/bot_api/auth.go`】

7.1 鉴权流程图

HTTP请求到达
  ↓
提取 Authorization: Bearer 
  │
  ├─ 无Authorization头 → 401
  ├─ token格式错误 → 401
  │
  ├─ token前缀 "app_" → App Bot鉴权路径
  │    ├─ 查库验证App Bot token
  │    ├─ 验证Bot状态(已发布?)
  │    └─ 通过 → 注入botInfo到context
  │
  ├─ token前缀 "bf_" / legacy → User Bot鉴权路径
  │    ├─ 查库验证User Bot token
  │    └─ 通过 → 注入botInfo到context
  │
  └─ 前缀无法识别 → 401
  │
  └─ token无效/过期/未发布 → 401(统一ErrBotAPIAuthFailed)

7.2 Bot类型对比

特性 App Bot User Bot
Token前缀 app_ bf_ / legacy
群组消息 ⚠️ app_bot_dm_only时仅支持私信 支持
发布要求 必须发布才能使用(bot_unavailable) 无需发布
插件市场 在插件市场发布 不经过插件市场

八、常见集成错误案例

案例1:发送消息返回401

# 错误请求(缺少Bearer前缀)
curl -X POST http://octo.example.com/v1/bot/messages/send \
  -H "Authorization: app_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{"recv_id": "xxx", "content": "hello"}'
# → 401 ErrBotAPIAuthFailed

# 正确请求
curl -X POST http://octo.example.com/v1/bot/messages/send \
  -H "Authorization: Bearer app_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{"recv_id": "xxx", "content": "hello"}'
# → 200 OK

案例2:卡片消息发送失败

// 错误:card_version不是1.5
{
  "type": 17,
  "card_version": "1.0",
  ...
}
// → ErrCardProfileUnsupported

// 正确
{
  "type": 17,
  "card_version": "1.5",
  "version": "1.5",
  ...
}

案例3:批量添加成员超限

# 错误:members数量201人
curl -X POST http://octo.example.com/v1/bot/groups/members/add \
  -H "Authorization: Bearer " \
  -d '{"group_id":"xxx", "member_ids":["u1","u2",...,"u201"]}'
# → 400 limit_exceeded

# 正确:分批,每批≤200人

案例4:长轮询用GET方法

# 错误:使用GET
curl http://octo.example.com/v1/bot/events?event_id=0
# → 404 Not Found

# 正确:使用POST
curl -X POST http://octo.example.com/v1/bot/events \
  -H "Authorization: Bearer " \
  -d '{"event_id":0, "limit":20, "wait":10}'
# → 200 OK

九、错误码快速查找索引

场景 HTTP码 code 章节
Token错误 401 ErrBotAPIAuthFailed 2.2
Bot未发布 403 bot_unavailable 2.3
不在群里 403 not_group_member 2.3
不是群管理员 403 not_group_admin 2.3
群已解散 403 group_disbanded 2.3
不在Space里 403 not_space_member 2.3
App Bot仅私信 403 app_bot_dm_only 2.3
非好友 403 not_friend 2.3
无编辑权限 403 message_edit_forbidden 2.3
群不存在 404 group_not_found 2.4
消息不存在 404 message_not_found 2.4
用户不存在 404 user_not_found 2.4
JSON格式错 400 request_invalid 2.1
批量超限 400 limit_exceeded 2.1
文件过大 400 file_too_large 2.1/6.4
文件类型错 400 file_type_unsupported 2.1/6.4
内容过大 400 content_too_large 2.1
请求体过大 413 payload_too_large 2.5
限流 429 (读Retry-After) 四
卡片过大 400 ErrCardTooLarge 三
卡片节点过多 400 ErrCardTooManyNodes 三
卡片版本错 400 ErrCardProfileUnsupported 三
服务内部错误 500 im_token_failed等 2.7

十、待确认项更新状态(2026-09-22 A类源码补证后)

项目 状态 说明
~~business/heartbeat默认限流参数~~ ✅已确认 per-bot层:business 20/200、heartbeat 1/10、register 0.5/10(默认dry-run观察模式);per-IP层:register 100/500、heartbeat 500/1500、batch 20/40
~~上传端点命名差异~~ ✅已解决 credentials与presigned两端点同时存在(bot_api.go:450-451),是两条路径非命名冲突
~~长轮询事件保留TTL~~ ✅已确认 默认7天(Robot.MessageExpire),队列key随新事件写入刷新TTL
~~文件类型白名单完整列表~~ ✅已确认 白名单约70种+黑名单31种,全文见§6.3
~~消息内容content_too_large阈值~~ ✅已确认 群MD 10240B/语音上下文 10000rune/OBO persona 4096B,详见§2.1
~~Bot token过期策略~~ ✅已确认 token无过期时间字段(robot表bot_token不过期,查询仅校验status=1),仅手动重置或Bot删除失效
~~metrics端点~~ ✅已确认 独立抓取服务器,默认:9090,env DM_METRICS_ADDR可调,见监控文档
Webhook回调失败重试机制 ⚠️待补充 平台级webhook非Bot主通道
各端版本兼容矩阵 ⚠️待Owner 官方release notes