{"record":{"id":"d60f503bc27df6e3","repo":"mihomo-party-org/clash-party","slug":"profileupdater-failed-to-update-loglabel","errorCode":null,"errorMessage":"[ProfileUpdater] Failed to update ${logLabel}:","messagePattern":"\\[ProfileUpdater\\] Failed to update (.+?):","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/main/core/profileUpdater.ts","lineNumber":66,"sourceCode":"  }\n}\n\nasync function auditPluginProfileVault(item: IProfileItem): Promise<void> {\n  if (item.type !== 'plugin' || !item.pluginId) return\n  try {\n    const { auditPluginVault } = await import('../resolve/plugin')\n    await auditPluginVault(item.pluginId)\n  } catch (e) {\n    await logger.warn(`[ProfileUpdater] Failed to audit plugin vault ${item.pluginId}:`, e)\n  }\n}\n\nfunction updateTask(itemId: string, logLabel: string): () => Promise<void> {\n  return async () => {\n    try {\n      await updateProfile(itemId)\n    } catch (e) {\n      await logger.warn(`[ProfileUpdater] Failed to update ${logLabel}:`, e)\n    }\n  }\n}\n\nfunction scheduleProfileUpdate(item: IProfileItem): void {\n  if ((item.type !== 'remote' && item.type !== 'plugin') || !item.autoUpdate || !item.interval)\n    return\n\n  const itemId = item.id\n  const logLabel = `profile ${itemId}`\n  const delayMs = intervalDelayMs(item.interval)\n  if (delayMs) {\n    intervalPool[itemId] = setInterval(updateTask(itemId, logLabel), delayMs)\n    return\n  }\n\n  if (typeof item.interval !== 'string') return\n","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/mihomo-party-org/clash-party/blob/911e090537acdf7c50bee1c3aebecc2ef119a8b5/src/main/core/profileUpdater.ts#L48-L84","documentation":"updateTask is the scheduled callback created by addProfileUpdater/scheduleProfileUpdate; it calls updateProfile(itemId) and swallows any rejection, logging this warning with a human label (logLabel). The scheduled update for a remote or plugin profile failed, but the failure is contained so the scheduler keeps running.","triggerScenarios":"The interval timer fires for a profile with autoUpdate enabled and updateProfile rejects: network failure reaching the remote URL, HTTP non-2xx, invalid downloaded content rejected by validation, or file-system write errors.","commonSituations":"Offline / captive portal at update time; subscription URL expired or returning 401/403; proxy not yet ready when the timer fires; DNS failure for the subscription host.","solutions":["Check the logged error `e` for HTTP status or network cause and test the subscription URL manually (curl).","Retry the update manually from the UI once connectivity is restored.","Fix the subscription source (renew URL/auth) or increase interval; verify updateProfile's validation rules accept the payload."],"exampleFix":"// before\nawait updateProfile(itemId)\n// after\ntry {\n  await updateProfile(itemId)\n} catch (e) {\n  if (isNetworkError(e)) scheduleRetry(itemId, 60_000)\n  else logger.warn(`Non-retryable profile update failure:`, e)\n}","handlingStrategy":"retry","validationCode":"const res = await fetch(item.url, { method: 'HEAD' }).catch(() => null)\nif (!res || !res.ok) {\n  logger.warn(`subscription unreachable for ${item.id}; deferring update`)\n  return\n}","typeGuard":null,"tryCatchPattern":"try {\n  await updateProfile(itemId)\n} catch (e) {\n  logger.warn(`[ProfileUpdater] Failed to update ${logLabel}:`, e)\n  scheduleRetryWithBackoff(itemId)\n}","preventionTips":["Schedule updates only when connectivity is available (net-status listener).","Renew subscription URLs/auth before expiry; monitor HTTP status codes.","Keep the swallow-and-log behavior for timers so one failure doesn't kill the scheduler."],"tags":["profile-update","scheduler","network","subscription"],"backgroundTag":"profile-update-failed","analyzedSha":"911e090537acdf7c50bee1c3aebecc2ef119a8b5","analyzedAt":"2026-08-30T13:00:49.174Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}