# 限制、错误码与重试规则

本页按当前聊天服务与客户端源码列出限制。数值是具体字段或路由的约束，不代表全局容量承诺；反向代理、操作系统和外部供应商还可能有独立限制，但本页不臆测未实现的策略。错误判断应结合HTTP状态、error.code和当前业务阶段。

## 单位与计数

MiB=1048576字节，KiB=1024字节。界面显示“10 MB”时，当前服务实际检查10×1024×1024字节。JSON上限计算传输字节，包含字段名、引号、转义与base64膨胀；不能仅计算正文字符。base64约为原字节数的4/3，必须规范标准编码，base64url不适用MLS payload。

多数服务端文字使用`[...text].length`计Unicode码点；iOS的String.count计字素簇，Android的length/take计UTF-16代码单元。组合重音和多码点表情等会产生不同计数。客户端允许输入不代表服务端一定接受，应保留服务端错误说明。依据：`chat-service/src/server.mjs:50-78,135-142`、`ios-app/Sources/Features/Moments/MomentComposerView.swift:97-100`、`android-app/app/src/main/java/cn/thestarline/workbench/MomentsScreens.kt:513-514`。

## 服务端大小与数量

| 对象 | 实际限制 | 超限表现 |
| --- | --- | --- |
| 默认JSON | 16384字节，根必须对象 | 413 TOO_LARGE；非JSON为415 CONTENT_TYPE |
| 普通消息 | trim后文字≤2000码点；text与一个mediaId二选一 | 400 INVALID_MESSAGE |
| 文件/聊天照片 | 单项10 MiB，非空 | 413 TOO_LARGE；空为400 EMPTY_FILE |
| 自定义贴纸 | 512 KiB；PNG/JPEG；PNG宽高1–2048 | INVALID_STICKER或TOO_LARGE |
| 聊天媒体账号配额 | 合计100 MiB；自定义贴纸100个 | 413 STORAGE_LIMIT；没有公开清理媒体配额的DELETE接口 |
| 文件名 | trim后1–120码点；无控制字符或路径分隔符 | 400 INVALID_NAME |
| 新事 | text≤2000码点；发布JSON≤20000字节；9图或单视频 | 400 INVALID_POST或413 TOO_LARGE |
| 新事媒体 | 图片10 MiB、视频30 MiB；每账号500 MiB | TOO_LARGE或STORAGE_LIMIT |
| 简介/资料受众 | bio≤160码点；selected最多100个不同当前好友 | INVALID_BIO或INVALID_VIEWERS |
| 个人群 | name为1–40码点；创建/邀请1–49个不同好友；全群≤50人 | INVALID_GROUP_NAME、INVALID_GROUP_MEMBERS或NOT_FRIENDS |
| 新事评论/举报 | 评论1–500码点，举报1–200码点 | INVALID_COMMENT或INVALID_REPORT |
| 记忆识别 | text为2–2000码点 | INVALID_MEMORY_TEXT |
| MLS密钥包 | TLS字节1–65536；JSON≤100000字节；每设备未用包≤25 | INVALID_KEY_PACKAGE、TOO_MANY_PACKAGES或TOO_LARGE |
| MLS事件 | payload 1–1048576字节；JSON≤1500000字节 | INVALID_EVENT、INVALID_CIPHERTEXT或TOO_LARGE |
| iOS独立MLS文字 | 非空、≤65536 UTF-8字节 | 本地invalidState；不适用于普通消息接口 |

依据：`chat-service/src/server.mjs:350-365,416-474,498-558,662-680,775-817,916-925`、`chat-service/src/database.mjs:219-226`、`chat-service/src/moments-repository.mjs:102-137`、`ios-app/Sources/Features/Chat/ChatCryptoDevice.swift:442-457`。

## 分页、有效期与已有频率计数

| 类型 | 值与边界 |
| --- | --- |
| 单聊/群消息 | 默认50、最多100；limit最少1；before与非零after不能混用，群不支持单聊after契约 |
| 普通events | 每批最多100，wait默认20秒、0–25秒；游标非负安全整数 |
| MLS events | 每批100、立即查询；sequence可合法跳号 |
| 新事feed | 默认20、最多50；comments和notifications每页30 |
| 通话历史 | 最近100条终态；无公开该路由分页游标 |
| 原生登录/聊天ticket/session | 10分钟/60秒/24小时；Account OIDC token期限另算 |
| 临时好友码/转出flow/媒体token | 10分钟/5分钟/30秒 |
| 呼叫与空房 | 约60秒未接超时；默认每10秒检查媒体空房，连续空约90秒结束 |
| 未绑定新事媒体 | 超过24小时可清理，不是所有已发布媒体24小时到期 |

当前`allowFriendLookup`按用户+method维护10分钟固定窗口：phone与temporary-code各最多20次，generate-code最多10次，memory-recognition最多10次。窗口从该方法当前计数周期第一次请求起计算；达到上限后不会因持续失败自动延长窗口。格式验证失败一般在计数前拒绝；格式正确但查不到目标也会计数，模型调用开始后失败可能已经计数。

未见对普通messages发送、全部GET、群操作或新事发布统一应用此计数器，也没有该代码自动返回Retry-After或剩余额度头的契约。不能把10分钟20次写成全站限流。依据：`chat-service/src/database.mjs:386-400`、`chat-service/src/server.mjs:775-790,832-898,1186-1190`。

## HTTP错误的触发条件

| HTTP / error.code | 典型触发条件 | 下一步 |
| --- | --- | --- |
| 400 INVALID_JSON / INVALID_INPUT | JSON无效、数组根、联系人添加方式不唯一、字段类型错误 | 修正请求结构，不能原样重试 |
| 400 INVALID_CURSOR / INVALID_LIMIT | 非安全整数、负游标、数量为0、混合前后游标 | 使用正确资源游标；最少1条 |
| 400 INVALID_PHONE / INVALID_FRIEND_ID / INVALID_FRIEND_CODE | 手机号、好友ID或临时码格式错误 | 本机规范化后再提交 |
| 400 INVALID_MESSAGE / INVALID_CLIENT_ID | text和media互斥失败、文字过长、UUID不合规、read目标不属于聊天 | 修正字段，不生成空消息 |
| 400 INVALID_POST / INVALID_COMMENT | 受众不符、媒体被绑定、图视频混传、回复不属于帖子 | 重读关系/资源；不要盲重发 |
| 400 NOT_FRIENDS | 建群成员未满足好友条件；邀请成员也可因加入后超过50人失败 | 分别检查目标用户UUID、当前好友关系与群容量，不能仅按“已断交”解释 |
| 400 INVALID_LOCATION / 409 NOT_SHARING | 坐标无效，或PUT前未开启共享 | 修正坐标；共享必须由用户显式开始 |
| 401 UNAUTHORIZED / INVALID_TICKET | 聊天session失效，或ticket过期、重放、proof错误 | 重新登录；不能删除proof降级 |
| 403 CONTACT_BLOCKED / NOT_FRIENDS | 联系动作中任一方屏蔽或当前不再是好友；建群/邀请的同码400例外见上行 | 刷新关系，停止发送/位置/通话动作 |
| 403 FORBIDDEN | 非群主或非企业授权角色等 | 使用真实有权身份，不绕过UI校验 |
| 404 NOT_FOUND / PROFILE_NOT_FOUND / NOT_MEMBER | 无记录、资源不可见、已退出范围等 | 刷新列表；不要枚举ID探测 |
| 409 CLIENT_ID_REUSED | 同幂等ID改了范围、正文、媒体或MLS密文 | 恢复原请求；新用户动作才用新ID |
| 409 ALREADY_FRIENDS / INCOMING_REQUEST | 已有好友关系或反向申请 | 进入既有会话或接受incoming |
| 409 E2EE_REQUIRED / NOT_ACTIVE | 非legacy尝试普通发送，或MLS尚未激活 | 走正确协议并补必要确认，不退回明文 |
| 409 CALL_BUSY / CALL_STATE | 另有通话或邀请、参与状态已变化 | 重查active/current，再决定动作 |
| 413 TOO_LARGE / STORAGE_LIMIT | 单次请求过大或账号媒体配额满 | 减小输入；重试不增加可用配额 |
| 415 CONTENT_TYPE / 416 INVALID_RANGE | JSON类型错误，或新事媒体Range语法/起止无效 | 改Content-Type或合法单段范围 |
| 429 TOO_MANY_LOOKUPS / TOO_MANY_CODES / TOO_MANY_RECOGNITIONS | 对应用户method计数达到上限 | 停止密集重试，等该窗口结束 |
| 503 LOGIN_UNAVAILABLE / E2EE_UNAVAILABLE / CALL_MEDIA_UNAVAILABLE / MEMORY_MODEL_UNAVAILABLE / CITY_STICKER_UNAVAILABLE | 缺配置、校验器/媒体/模型/素材不可用等 | 保留本机状态，检查具体依赖，适度重试 |
| 500 INTERNAL | 未被路由映射的异常 | 保留脱敏上下文，先确认是否实际写入 |

MLS错误映射存在细节：NOT_MEMBER→404、UNKNOWN_DEVICE→403，INVALID_KEY_PACKAGE/INVALID_EVENT/INVALID_RECIPIENT/INVALID_WELCOME→400，其余具名错误通常409。因此INVALID_COMMIT当前是409，不能只按“INVALID前缀都400”分类；缺devices查询deviceId的INVALID_DEVICE为400，而包可用性内部的同名错误走409映射。依据：`chat-service/src/server.mjs:1193-1203`。

发送权限与历史读取权限并不对称。CONTACT_BLOCKED/NOT_FRIENDS约束相关发送、位置或通话动作，不能推导普通旧附件必然404；普通单聊媒体凭仍可见消息读取时没有重新查好友/屏蔽，所有者还可独立下载。新事媒体则重新按帖子可见性过滤。依据：`chat-service/src/database.mjs:239-254`、`chat-service/src/moments-repository.mjs:117-123`。

## 重试必须区分是否有幂等键

正常幂等例：普通消息POST使用相同clientId和相同内容，第一次响应丢失后重试返回200及原message；MLS沿用同clientId、同密文和同目标重试返回原sequence。不能重新加密后沿用旧ID。GET可恢复查询，已读只前进，PUT like设置同一状态可重复。

失败风险例：新事POST、评论POST与媒体上传没有同等clientId契约，响应丢失后直接重发可能重复内容或额外占用配额；先查询最新记录确认。生成好友码重试会替换旧码；申请接受重试可能因原申请已删除而404，此时应查会话而非认为好友建立必失败。换票响应丢失后ticket可能已消费，重试401需重新发起授权，不能恢复出未收到的token。

不要将401自动重试成无限登录循环，也不要把所有503视为无副作用：关系操作在媒体调用阶段失败与消息提交后刷新失败有不同边界。本机退出成功也不能证明远端logout成功；Account改密不自动撤销聊天session。依据：`chat-service/src/database.mjs:524-551,635-652`与[登录与会话](https://document.thestarline.cn/app/authentication.md)。

## 当前客户端差异

Android新事先选selected好友再切到其他visibility时，仍可能携带非空viewerIds，服务端返回400 INVALID_POST；iOS发布前在非selected时发送空列表。iOS独立MLS prepare遇blocked只返回模式，不自动完成重键，继续应用发送会因未active而拒绝。这些是本轮源码核出的失败路径，未运行或修复原生代码，不能标成验收通过。对应依据见[新事](https://document.thestarline.cn/app/moments.md)和[MLS协议](https://document.thestarline.cn/app/mls-protocol.md)。

## 客户端时限与验收

通用iOS JSON请求通常15秒、普通/events为32秒；聊天媒体60秒，新事媒体90秒。Android连接15秒，普通JSON读20秒、events读35秒、上传下载读90秒。它们是网络调用时限，不是文档或媒体预览的自动关闭时间，也不代表服务器一定在时限内取消已提交操作。依据：`ios-app/Sources/Features/Chat/ChatAPI.swift:45-96`、`ios-app/Sources/Features/Moments/MomentsStore.swift:186-227`、`android-app/app/src/main/java/cn/thestarline/workbench/AppData.kt:26-94`。

当前两端错误包装主要保留HTTP状态与message，并非都向业务层保留error.code；排错应记录已脱敏的状态、阶段与资源类型，不能记录Bearer、ticket、verifier或用户正文。字段全表见[API参考](https://document.thestarline.cn/app/api-reference.md)，恢复步骤见[故障处理](https://document.thestarline.cn/app/incident-runbooks.md)，场景验证见[验收矩阵](https://document.thestarline.cn/app/qa-matrix.md)。本轮为源码与文档核对，未执行生产业务或原生验收。

源码核对范围：2026-10-23 的当前实现。本轮未执行生产业务请求或原生设备验收。
