{"record":{"id":"64c9dbfe9e46fd0e","repo":"jlcodes99/cockpit-tools","slug":"workbuddyautocheckin-64c9db","errorCode":null,"errorMessage":"[WorkbuddyAutoCheckin] 监听后端签到日志事件失败:","messagePattern":"\\[WorkbuddyAutoCheckin\\] 监听后端签到日志事件失败:","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/components/codebuddy-suite/WorkbuddyAutoCheckinConfigModal.tsx","lineNumber":96,"sourceCode":"          setLogsLoading(false);\n        }\n      }\n    };\n\n    void loadLogs();\n    const handleLogsChange = () => {\n      void loadLogs();\n    };\n    void listen(WORKBUDDY_AUTO_CHECKIN_LOGS_CHANGED_EVENT, handleLogsChange)\n      .then((stopListening) => {\n        if (disposed) {\n          stopListening();\n        } else {\n          unlisten = stopListening;\n        }\n      })\n      .catch((err) => {\n        console.warn('[WorkbuddyAutoCheckin] 监听后端签到日志事件失败:', err);\n      });\n    return () => {\n      disposed = true;\n      unlisten?.();\n    };\n  }, []);\n\n  const handleSave = async () => {\n    const startMin = parseTimeToMinutes(startTime);\n    const endMin = parseTimeToMinutes(endTime);\n\n    if (startMin > endMin) {\n      setError(t('workbuddy.checkin.timeRangeError', '开始时间不能晚于结束时间'));\n      return;\n    }\n\n    setSaving(true);\n    setError(null);","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/jlcodes99/cockpit-tools/blob/1ed8b77992d62ca81fabf744deb0839ad361d5bf/src/components/codebuddy-suite/WorkbuddyAutoCheckinConfigModal.tsx#L78-L114","documentation":"WorkbuddyAutoCheckinConfigModal subscribes to backend auto-checkin log events on mount. If the listener registration promise rejects (event channel unavailable or backend not ready), the catch logs the failure; the modal then only updates logs via manual loads rather than live events.","triggerScenarios":"The event-listen promise rejects in the modal's mount effect: backend event emitter not registered, wrong event name, or the effect's cleanup ran before registration resolved (unlisten returned as the promise value).","commonSituations":"Modal opened before backend service warm-up; event name refactored on one side only; hot-reload/dev webview reload killing pending subscriptions.","solutions":["Verify both frontend and backend use the identical event name string.","Delay or retry subscription until the backend confirms readiness (e.g. after an initial health/config load succeeds).","Ensure cleanup handles the case where the listen promise resolves with the unlisten function after dispose (the `stopListening` branch).","Fall back to polling `loadLogs` when live subscription fails."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"const backendReady = await invoke('backend_ready').then(() => true).catch(() => false);\nif (!backendReady) scheduleSubscribeRetry(1000);","typeGuard":"function isLogEvent(e: Event): e is CustomEvent<CheckinLog> {\n  return e instanceof CustomEvent && e.detail != null;\n}","tryCatchPattern":"const subscribe = (attempt = 0) =>\n  listen(LOGS_EVENT, handler)\n    .then((stop) => { disposed ? stop() : (unlisten = stop); })\n    .catch((err) => {\n      if (attempt < 3) setTimeout(() => subscribe(attempt + 1), 1000 * 2 ** attempt);\n      else console.warn('[WorkbuddyAutoCheckin] 监听后端签到日志事件失败:', err);\n    });","preventionTips":["Retry event subscription with exponential backoff on failure","Fall back to polling loadLogs when subscription fails permanently","Synchronize event names between frontend and backend via shared constants","Verify backend readiness before subscribing on mount"],"tags":["tauri","event-listener","ipc","subscription"],"backgroundTag":"event-listener-registration-failed","analyzedSha":"1ed8b77992d62ca81fabf744deb0839ad361d5bf","analyzedAt":"2026-09-05T09:51:41.178Z","contentChangedAt":"2026-09-05T09:51:41.178Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}