任务刚启动就报登录失效,但浏览器页面还停在已登录状态,这两件事并不冲突:报错来自执行任务的那一侧会话,比如请求头里的 Cookie、本地保存的会话令牌或独立的浏览器上下文;页面显示的是另一份会话。判断方向是:先确认两边是不是同一份凭证,再确认这份凭证在发起任务那一刻是否仍然有效,而不是先去改账号或重装工具。
Claude Duet 这类“报登录态失效、页面却还是登录中”的情况,通常是任务侧复用的会话与页面当前会话对不上:拿的是旧快照、Cookie 缺项,或者上下文过期后没有刷新。做法是先固定路径复现并记下时间点,再看会话记录里最后一个成功动作之后,跟的是过期还是跳转,然后用只读检查脚本在执行前判断会话是否有效,最后改会话复用方式并连跑两次对比,才能确认不是碰巧跑通。
复现失效并记下发生的时间点
偶发报错最难处理,因为事后无法判断是会话过期、页面跳转,还是任务参数拼错。建议先把触发条件固定下来:同一个入口页面、同一个浏览器配置文件、同一条操作顺序,连续做两到三次,只有能稳定重复的那条路径才值得继续排查。如果清了登录状态重登一次就不再报错,那本身就说明问题跟旧会话快照有关,可以把这一点记进记录里。
复现时按下表记录,页面地址和操作顺序必须同时记录,因为登录态丢失往往出现在某次跳转之后,光看报错无从对齐。
复现记录模板
- 发生时间(含时区):
- 浏览器 / 客户端配置文件:
- 进入任务前的页面地址(完整 URL):
- 操作顺序:1) 打开X 2) 登录/复用会话 3) 触发任务 4) 报错
- 上一次成功的时间点:
- 报错原文(整段复制,不转述):
- 报错出现在第几步操作之后:
- 报错时页面地址是否变化(变/未变):
- 会话标识在报错前后是否变化(可见则填):
- 本次是否为清空会话后重新登录:是/否记录时尽量只抓关键字:报错文案里区分“未登录”和“登录已过期”的说法往往对应不同处理;页面地址里如果带着租户、工作区之类的路径参数,也要一并抄下来,因为这些参数常常决定会话在哪个域下被认。
在会话记录里定位失效前最后一个成功动作
会话记录按时间戳升序读,读法是寻找分界线,而不是逐条看完所有条目:先找到最后一条返回正常业务响应的请求,再看它后面的第一条异常条目,两者之间发生了什么,就是失效点。这样读比从报错往回翻更快,也能避免把早先的无关失败当成原因。
两类原因在记录里留下的特征不一样,可以先按下面两条判断:
- 会话过期:最后一条成功请求与失败条目之间有一段明显静默间隔,失败条目返回的是未授权类状态,失败条目里携带的会话标识与前面成功条目是同一份,页面地址没有变化。这类情况处理方向是刷新或重新获取会话,不必查页面逻辑。
- 页面跳转导致状态丢失:失败条目紧跟在一次跨路径或跨域跳转、重定向链之后,失败条目里的会话标识为空、或者换成了另一份,失败条目请求的域名或路径与最后一条成功请求不一致。这类情况处理方向是让任务侧复用的会话与跳转后页面所在的域一致,或者让流程在跳转后重新读取会话。
两种特征可能同时出现,比如跳转之后恰好也过期了。此时先修更靠前的那一个:跳转导致的域不一致没解决,刷新多少次会话都可能在下一个跳转处再丢一次。
写一段检查登录态的通用脚本骨架
与其等任务报错再回头查,可以在执行前先判断当前会话是否有效。下面是一段伪代码骨架,只约定输入输出与失败分支,具体探测地址和执行环境需要结合自己的接入方式替换;探测用的接口或页面建议选只读、不产生业务数据的那个。
// 执行前检查,不发起业务任务
function assertSessionUsable(ctx) {
// ctx: { cookieHeader, token, pageUrl, savedAt }
const problems = [];
if (!ctx.cookieHeader && !ctx.token) problems.push('no-credential');
if (!ctx.pageUrl) problems.push('no-origin');
// 1) 只读探测:换成环境里无副作用的页面或接口
const probe = requestReadOnly(PROBE_PATH, {
cookie: ctx.cookieHeader,
token: ctx.token,
referer: ctx.pageUrl
});
// 2) 判断
if (probe.networkError) problems.push('network');
if (probe.status === UNAUTHORIZED) problems.push('expired-or-mismatch');
if (probe.redirectedToLogin) problems.push('redirected-to-login');
// 3) 失败分支:不重试业务任务,先把问题暴露出来
if (problems.length) {
log('session-precheck-failed', { problems: problems, savedAt: ctx.savedAt });
return { ok: false, problems: problems };
}
return { ok: true };
}这段骨架的用处是把“登录态失效”提前到任务开始之前暴露,并把原因分成无凭证、网络不通、被判定未授权、被跳转到登录页四类。失败时不要自动重试业务任务,那只会把过期问题掩盖成随机失败。拿到 problems 后,再回到第二步的会话记录去对时间点。
修正会话复用方式后重新验证
常见的复用方式问题有两类:一是直接读一份保存时间较久的会话快照,中间页面已经跳转或刷新过;二是任务侧和页面侧分别取会话,两者本就不是同一份。修正方向可以先做保守的:每次任务启动前重新从当前页面上下文读取会话,而不是读历史快照;如果流程中会发生跨路径跳转,就在跳转后重新读取一次,不要沿用跳转前的会话。
验证分三步走,缺一步都可能得出“修好了”的错觉:
- 验证动作:清掉旧的会话快照,重新走一次正常登录,导出会话后立刻执行一次预检脚本,预检通过再跑任务。
- 成功表现:预检返回 ok,任务发出的第一个请求不再返回未授权,且会话记录里第一条请求的域名和路径与报错前那次跳转后的页面地址一致。
- 连跑两次对比:同一天内连续跑两次,两次都做预检并记录结果。若第一次通过、第二次失败,说明会话仍然依赖保存时间,属于快照过期或跳转后未刷新,需要回到第二步重新确认失效点;只有两次预检都通过且任务首个请求都正常,才能认为复用方式改对了。
边界也要说清楚:把过期时间调长、在任务里加一次自动重试,都只能暂时缓解,不能替代会话复用方式的修正,而且会让日志里的失败原因更难分辨。会话凭证本身建议按环境存放并控制可读范围,不要在日志里打印完整内容,只保留前后几位和保存时间,便于对照时间点。