跳转至

[ISSUE-0036] 创建任务 toast 成功但任务列表没增加

用户反馈:点击「+创建任务」选了 111923 个地区 → toast 提示「任务已创建(111923 个地区)」→ 但任务列表/KPI 没增加。

用户感知

1. 点"创建任务"按钮
2. 配置:关键词 + 国家/州(实际选了所有美国地区 = 111923)
3. 点提交 → toast: ✅ 任务已创建(111923 个地区)
4. ❌ 任务列表无变化,KPI 仍 "1/1"

根因(双重失败)

Bug 1:addTask 静默吞错

src/utils/task-store.ts:61-69(v0.10.54 之前):

export function addTask(task: MapTask): Promise<void> {
  writeChain = writeChain
    .then(async () => {
      const list = await getTasks();
      list.unshift(task);
      await taskListItem.setValue(list);  // ← 超 chrome.storage 配额会 throw
    })
    .catch(() => {});  // ❌ 静默吞掉错误
  return writeChain;
}

taskListItem.setValue(hugeList) 超 chrome.storage.local 配额 → throw → .catch(() => {}) 吞掉 → caller 拿到 resolved Promise → background await createTask() 也 resolved → return {success: true} → dialog toast 显示成功。

Bug 2:单任务无地区数上限

chrome.storage.local: - 每 item 限制约 5-10MB(具体 Chrome 版本不同) - 整个 storage 配额约 10MB

111923 个 locationEntries × ~80 字节 = ~9MB,单 item 直接撑爆。

修复(三层)

Layer 1:addTask 错误传播

export function addTask(task: MapTask): Promise<void> {
  // 保留 writeChain 串行写入语义,但错误必须传播
  const next = writeChain.then(async () => {
    const list = await getTasks();
    list.unshift(task);
    await taskListItem.setValue(list);
  });
  // writeChain 自己保持 OK 状态(不影响后续 addTask),但 next 暴露错误给 caller
  writeChain = next.catch(() => {});
  return next;  // ← 这里返回的 promise 会 reject
}

关键技巧:分两个 promise — next 给 caller(保留 reject),writeChain 给后续 chain(吞错防 chain dead)。

Layer 2:background create-task handler try-catch

if (type === 'create-task') {
  try {
    await createTask(message.task || {});
    return { success: true };
  } catch (e: any) {
    console.error('[bg] create-task failed:', e);
    return { success: false, message: e?.message || '创建任务失败(storage 可能超配额)' };
  }
}

Layer 3:守门 + 友好错误

createTask 入口:

const MAX_LOCATIONS_PER_TASK = 50_000;
const locCount = isMulti ? locationEntries.length : locations.length;
if (locCount > MAX_LOCATIONS_PER_TASK) {
  throw new Error(
    `单任务地区数超上限(${locCount.toLocaleString()} > 50,000)— ` +
    `请拆分为多个任务(按国家 / 州分批)。chrome.storage 单 item 限 ~5MB,过大会写失败。`
  );
}

Layer 4:dialog 检查返回

const resp: any = await browser.runtime.sendMessage({ type: 'create-task', task: ... });
if (resp && resp.success === false) {
  notice.error(`创建失败:${resp.message || '未知错误'}`);
  return; // 保持 dialog 打开
}
notice.success(`任务已创建(${locationEntries.length} 个地区)`);

修复后流程(111923 地区场景)

1. 用户提交 → sendMessage create-task
2. background createTask 入口检查 locCount(111923) > 50000 → throw
3. handler try-catch → return { success: false, message: "单任务地区数超上限..." }
4. dialog 收到 → notice.error("创建失败:单任务地区数超上限...")
5. dialog 不关,用户改小重试

改动文件

文件 改了什么
src/utils/task-store.ts addTask 错误传播(分 next + writeChain)
src/entrypoints/background/task-manager.ts createTask 入口加 MAX_LOCATIONS_PER_TASK 守门
src/entrypoints/background/index.ts create-task handler 加 try-catch 返回 success/error
src/sections/task/task-create-dialog.tsx 检查 sendMessage 返回,失败 toast.error
package.json 0.10.53 → 0.10.54

验证

  • pnpm compile 0 错
  • pnpm build 7.52s
  • 📋 浏览器实测:
  • 选 111923 地区 → 应弹 error toast「单任务地区数超上限」
  • dialog 不关,用户能改小再试
  • 选合理数量(如 1000)→ 正常创建 + 任务列表增加

元-观察:连续 3 个用户使用反馈

v0.10.52 ISSUE-0034: Failed to fetch 冒 chrome 错误页
v0.10.53 ISSUE-0035: 按钮无反应(storage API 混用)
v0.10.54 ISSUE-0036: toast 成功但实际失败(addTask 吞错 + 无守门)

5 轮 agent + 5 工具 + 5 rule 收敛后,3 个用户使用反馈连击找到 bug。模式: - ISSUE-0034: 副作用层(chrome 错误页 UX) - ISSUE-0035: API 一致性(两套合法 API 混用) - ISSUE-0036: 错误传播(静默吞错 + 缺守门)

这些都是代码看起来正确实际行为不对的 bug — audit 找不到,必须真实使用。

audit_grep 防同款

audit_grep:
  - pattern: "writeChain\\s*=\\s*writeChain\\s*\\.\\s*then.*\\.\\s*catch\\(\\(\\)\\s*=>\\s*\\{\\}\\)"
    description: "writeChain 静默吞错模式"

未来如果有人写新的 writeChain = writeChain.then(...).catch(() => {})pnpm scan:issue-coverage 会扫到提示。

如何避免再犯

  • storage write 不要静默吞错 — caller 需要知道是否真成功
  • 跨 sendMessage 边界的操作必须检查 response.success — 不要假设成功
  • 数据量上限要在入口守门 — 不要让超大 payload 一路传到 storage 才报错
  • 写 toast 之前 await 完整链路结果 — 不要 await 一半就 toast

相关

  • [[0035-create-task-btn-storage-api-mix|0035-去创建任务按钮无反应-storage-api混用]] — 同期用户实测反馈
  • chrome.storage.local 配额限制:每 item 约 5-10MB,整库约 10MB