{"record":{"id":"e1bf0e7308f8abf4","repo":"jlcodes99/cockpit-tools","slug":"update-canceled-by-user","errorCode":"UPDATE_CANCELED_BY_USER","errorMessage":"UPDATE_CANCELED_BY_USER","messagePattern":"UPDATE_CANCELED_BY_USER","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/App.tsx","lineNumber":1784,"sourceCode":"    updateDownloadOwnerRef.current = 'shared';\n    setUpdateRetryStatus('');\n    setUpdateDownloadError('');\n    setUpdateErrorDetails('');\n    setUpdateAction({\n      state: 'downloading',\n      version: expectedVersion,\n      progress: 0,\n      requiresInstall: true,\n    });\n    writeUpdateLog('info', `统一更新任务开始下载: version=${expectedVersion}`);\n\n    let usedAttempts = 0;\n    try {\n      const downloadedUpdate = await retryWithBackoff(\n        async (attempt) => {\n          usedAttempts = attempt;\n          if (updateCancelRequestedRef.current || updateDownloadTaskIdRef.current !== taskId) {\n            throw createUpdaterCanceledError();\n          }\n\n          let candidate: UpdaterUpdate | null = null;\n          try {\n            candidate = await runUpdaterCheck();\n            if (!candidate) {\n              throw new Error('No update available from updater plugin');\n            }\n            activeUpdateDownloadRef.current = candidate;\n\n            const candidateVersion = candidate.version;\n            const { releaseNotes, releaseNotesZh } = parseUpdaterReleaseNotes(candidate.body);\n            await invoke('save_pending_update_notes', {\n              version: candidateVersion,\n              releaseNotes,\n              releaseNotesZh,\n            }).catch((error) => {\n              console.error('[App] Failed to cache shared update notes:', error);","sourceCodeStart":1766,"sourceCodeEnd":1802,"githubUrl":"https://github.com/jlcodes99/cockpit-tools/blob/1ed8b77992d62ca81fabf744deb0839ad361d5bf/src/App.tsx#L1766-L1802","documentation":"Cancellation sentinel in MainApp's unified update downloader: inside the retryWithBackoff attempt callback, updateCancelRequestedRef is set or the download task id no longer matches (superseded by a newer task), so UPDATE_CANCELED_BY_USER is thrown to unwind the retry loop immediately. It distinguishes a deliberate cancel/staleness abort from a real download failure.","triggerScenarios":"Thrown at src/App.tsx:1784 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["No action needed when the user cancelled — the UI state was already reset before the throw","If it appears without user action, check whether another update task superseded this one (task-id mismatch)","Ensure catch handlers treat this sentinel as control flow, not as an error to log/redact"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"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"}