{"record":{"id":"8d02915009d7d160","repo":"mihomo-party-org/clash-party","slug":"missing-profile-update-handler","errorCode":null,"errorMessage":"Missing profile update handler","messagePattern":"Missing profile update handler","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/renderer/src/components/profiles/edit-info-modal.tsx","lineNumber":66,"sourceCode":"  const { t } = useTranslation()\n  const isImportMode = mode === 'import'\n  const canSave =\n    !isImportMode || (values.type === 'remote' ? Boolean(values.url?.trim()) : values.file != null)\n\n  const onSave = async (): Promise<void> => {\n    try {\n      const updatedItem = {\n        ...values,\n        override: values.override?.filter(\n          (i) =>\n            overrideItems.find((t) => t.id === i) && !overrideItems.find((t) => t.id === i)?.global\n        )\n      }\n      if (isImportMode) {\n        if (!addProfileItem) throw new Error('Missing profile import handler')\n        await addProfileItem(updatedItem)\n      } else {\n        if (!updateProfileItem) throw new Error('Missing profile update handler')\n        await updateProfileItem(updatedItem)\n        await addProfileUpdater(updatedItem)\n        await mihomoHotReloadConfig()\n      }\n      onClose()\n    } catch (e) {\n      toast.error(String(e))\n    }\n  }\n\n  const selectLocalFile = async (): Promise<void> => {\n    const files = await getFilePath(['yml', 'yaml'])\n    if (!files?.length) return\n\n    const file = await readTextFile(files[0])\n    const fileName = files[0].split('/').pop()?.split('\\\\').pop()\n    setValues({\n      ...values,","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/mihomo-party-org/clash-party/blob/911e090537acdf7c50bee1c3aebecc2ef119a8b5/src/renderer/src/components/profiles/edit-info-modal.tsx#L48-L84","documentation":"The edit branch of onSave() requires updateProfileItem; if it is undefined the modal throws 'Missing profile update handler'. Same pattern as the import-mode guard: the context providing profile mutation functions is missing or incomplete when the user saves an EDITED profile.","triggerScenarios":"Submitting an edit of an existing profile while the modal is rendered outside the provider supplying updateProfileItem, or the context value was constructed without that function.","commonSituations":"Portal-rendered modal above the provider; hook rename/refactor left the provider value without updateProfileItem; provider conditionally unmounted during save (e.g. modal triggers state change that unmounts provider).","solutions":["Ensure the modal is a descendant of the context provider that defines updateProfileItem.","Inspect the provider's value and confirm updateProfileItem is included.","Pass updateProfileItem via props as an explicit alternative to context.","Check for provider unmount during async save and stabilize the provider placement."],"exampleFix":"// before\nif (!updateProfileItem) throw new Error('Missing profile update handler')\n// after\nconst { updateProfileItem } = useProfilesContext()\nif (!updateProfileItem) throw new Error('Missing profile update handler') // now only fires on real wiring bugs","handlingStrategy":"type-guard","validationCode":"if (typeof updateProfileItem !== 'function') {\n  console.error('edit-info-modal: updateProfileItem missing — check provider wiring')\n  return\n}","typeGuard":"function hasUpdateHandler(h: unknown): h is (item: ProfileItem) => Promise<void> {\n  return typeof h === 'function'\n}","tryCatchPattern":"try {\n  if (typeof updateProfileItem === 'function') {\n    await updateProfileItem(updatedItem)\n    await addProfileUpdater(updatedItem)\n    await mihomoHotReloadConfig()\n    onClose()\n  }\n} catch (e) {\n  notify.error(String(e))\n}","preventionTips":["Keep modal components as descendants of the profiles provider.","Type the context value strictly (updateProfileItem: NonNullable<...>) to catch omissions at compile time.","Test the edit-profile save path with the real provider wrapper.","Avoid unmounting the provider while an async save is in flight."],"tags":["react","context","missing-handler","ui"],"backgroundTag":"missing-context-handler","analyzedSha":"911e090537acdf7c50bee1c3aebecc2ef119a8b5","analyzedAt":"2026-08-30T13:00:49.174Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}