定时发布到点却提示账号未登录或发布失败时,先不要急着重新扫码登录。打开 Fableclip 的发布任务记录,找到第一条失败任务,看它是卡在内容生成、素材上传,还是提交发布这一步——这一步决定了后面该查授权页还是查发布队列。合理的顺序是:先定位失败环节,再核对账号授权状态与令牌有效期,接着看发布队列长度和并发上限,最后拿平台返回的失败原因做分类处理。三处都排除之前批量重新授权,会把本来只是排队堵住的账号一起改掉,反而放大问题。
定时发布提示掉线,多数时候不是账号真被登出,而是发布链路中某一环先失败了:令牌过期会让提交发布直接返回未授权,队列积压会让任务长时间停在等待状态并被误读成掉线,平台侧审核或频率限制则是另一类。判断顺序建议固定为发布任务记录 → 授权状态 → 队列与并发 → 平台返回原因。授权过期需要重新授权并确认令牌刷新成功;队列问题先降并发、看位置是否推进;平台拒绝按原因分类处理,不盲目重试。所有结论以你自己的任务记录、授权页和队列页面为准。
在发布任务记录里找出第一条失败任务
不要从最新一条失败任务开始看,要从最早的一条失败任务开始看。最早失败的任务往往才是原因,后面的一串失败大多是它的延续,比如第一条任务是令牌过期,后续任务跟着全部返回未授权。任务记录至少要看这几个字段:
- 触发时间:区分是定时触发还是手动触发,确认失败发生在计划时间点附近
- 账号标识:同一批发布是否集中在某一个账号,还是所有账号一起失败
- 内容标题或素材 ID:确认是否只对某一条视频失败,例如素材本身就是平台不接受
- 状态与阶段:任务状态之外,还要看它停在生成、上传还是提交发布哪个阶段
找到第一条失败任务后,把失败任务前后各一条记录拉出来对比,逐项看差异:
| 对比项 | 失败任务 | 前后成功记录 |
|---|---|---|
| 账号 | 是否同一个账号 | 账号是否不同 |
| 卡住的阶段 | 停在生成 / 上传 / 提交发布 | 是否走完全部阶段 |
| 返回信息 | 有无未授权、超时、被拒绝字样 | 是否为空 |
| 时间间隔 | 距上一条成功记录多久 | 正常发布间隔 |
如果失败集中在同一账号、且停在提交发布这一步并返回未授权,先看授权状态;如果所有账号同一时间段一起卡住,优先怀疑队列和并发,而不是逐个账号掉线。
核对账号授权状态与令牌有效期
授权页通常能看到以下字段,具体名称以你所用版本为准:账号标识、当前授权状态(已授权 / 已失效 / 需要重新登录)、令牌签发时间、令牌过期时间、最近一次刷新时间、平台侧的提示文案。常见过期提示大意是“账号授权已失效,请重新登录”或“登录状态已过期”,看到这类文案基本可以判定为凭据问题,不是队列问题。
区分“授权过期”和“被平台主动登出”,看两点:一是最近一次刷新时间是否在过期时间之前、刷新是否返回成功;二是重新授权后能否正常拉取账号信息。能刷新成功并恢复发布,属于过期;重新授权后仍然立刻失效,通常是被平台侧登出或凭据被顶掉,需要检查是否有其他地方在同时使用同一个账号。
# 通用骨架,接口名与字段均为占位,按实际接入替换
def refresh_credential(account_id):
resp = call_api("/placeholder/auth/refresh", {"account_id": account_id})
if resp.get("ok"):
save_credential(account_id, resp["credential"])
log(account_id, "refresh_ok", resp.get("expires_at"))
return True
log(account_id, "refresh_fail", resp.get("error"))
return False
# 验证:刷新后重新读取一次授权状态,不要只看刷新接口的返回值
status = call_api("/placeholder/auth/status", {"account_id": account_id})
print(status.get("state"), status.get("expires_at"))
刷新之后必须重新读一次授权状态,确认状态从失效变成已授权、过期时间已经后移,再触发一个测试发布任务。只看刷新请求返回成功,很容易漏掉服务端没真正写入凭据的情况。
检查发布队列积压与并发上限
队列堆积造成的现象和掉线很像:任务一直处于等待或进行中,界面看起来就是“没发出去”。在 Fableclip 的发布队列页或设置页里,先看两类信息:当前队列长度(等待中、进行中、失败各多少)和发布并发上限(同时发布的任务数)、单账号并发限制。队列长度持续增长、并发上限又设得比较高,说明任务在互相挤占。
# 通用轮询骨架,用于确认队列是否在推进
def poll_queue_status(queue_api, task_id, rounds=2, interval=15):
history = []
for _ in range(rounds):
s = queue_api(task_id) # 期望返回 state / position 等字段
history.append((s.get("state"), s.get("position")))
time.sleep(interval)
return history
# 连续两次查询,看状态是否从 pending 走向 running/done,位置是否前移
print(poll_queue_status(fetch_task_status, "task_placeholder_id"))
验证方式就是连续两次查询同一个任务:状态有推进、排队位置在变小,说明队列在工作,掉线提示是表象;两次查询完全一致且队列长度在涨,才需要看并发设置,先把并发调低,再逐步加回,观察队列是否恢复流动。
对照平台返回的失败原因做分类处理
把平台返回的原始错误信息记录下来,按三类分开处理,不要用同一个重试策略套所有情况。
| 失败类型 | 典型返回 | 处理动作 |
|---|---|---|
| 审核拒绝 | 内容不合规、被驳回 | 不自动重试,改素材或文案后人工重新提交 |
| 素材格式不符 | 格式、时长、分辨率不支持 | 不自动重试,转码或用合规素材替换后重发 |
| 频率限制 | 请求过于频繁、稍后再试 | 可以自动重试,按可配置间隔退避 |
| 未授权 | 登录过期、invalid token | 先刷新令牌,刷新失败再转人工授权 |
重试间隔不要写死成固定值,放在配置里按任务类型区分,例如 retry_interval_seconds、retry_backoff_factor、max_retry_interval_seconds,频率限制类任务按倍数逐次拉长,审核和素材类任务直接不进重试队列。
设定重试与人工兜底的边界
重试是为了覆盖偶发的网络抖动和短时限流,不是为了掩盖授权失效和素材问题。建议给每个任务设一个明确的重试次数上限(例如放在配置项里,从两三次起步,按自身发布量调整),超过上限就进入失败列表并标记需要人工处理,不要无限循环。
转人工建议按三个触发条件判断:一是刷新令牌后重新读取授权状态仍为失效;二是同一任务因频率限制重试到上限仍未成功;三是失败原因落在审核拒绝或素材格式不符这两类,本身就不应自动重试。命中任意一条,就停下自动流程,等人处理。
日志至少保留这些字段,便于事后复盘:任务 ID、账号标识、内容标题或素材 ID、失败环节(生成 / 上传 / 提交发布)、平台原始错误码与错误信息、重试次数、每次重试的时间戳、令牌最近刷新时间与结果、队列长度快照。字段齐了,下次再遇到掉线提示,直接按这四步走一遍就能分清是授权、队列还是平台侧的问题。