{"record":{"id":"2e7dcd7dfba4e6e6","repo":"tinyhumansai/openhuman","slug":"skills-default-channel-persist-failed","errorCode":null,"errorMessage":"[skills] default channel persist failed:","messagePattern":"\\[skills\\] default channel persist failed:","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/src/pages/Skills.tsx","lineNumber":559,"sourceCode":"    },\n    [location.pathname, location.search, navigate]\n  );\n  const dispatch = useAppDispatch();\n  const [defaultChannelBusy, setDefaultChannelBusy] = useState<ChannelType | null>(null);\n  const handleSetDefaultChannel = useCallback(\n    async (channel: ChannelType) => {\n      // Single-flight: ignore re-entries while a write is in progress so two\n      // back-to-back clicks can't interleave (would leave UI + persisted\n      // preference disagreeing on which channel won).\n      if (defaultChannelBusy !== null) return;\n      setDefaultChannelBusy(channel);\n      try {\n        // Persist first, then dispatch — on failure the UI keeps the previous\n        // selection and the user sees no false-positive flicker.\n        await channelConnectionsApi.updatePreferences(channel);\n        dispatch(setDefaultMessagingChannel(channel));\n      } catch (err) {\n        console.warn('[skills] default channel persist failed:', err);\n      } finally {\n        setDefaultChannelBusy(null);\n      }\n    },\n    [dispatch, defaultChannelBusy]\n  );\n  const { definitions: channelDefs } = useChannelDefinitions();\n  const channelConnections = useAppSelector(state => state.channelConnections);\n\n  const {\n    toolkits: composioToolkits,\n    // Default to an empty map so the component is resilient when a test\n    // mock (or an older hook build) omits the dynamic-catalog field.\n    catalogByToolkit: composioCatalogByToolkit = new Map(),\n    connectionByToolkit: composioConnectionByToolkit,\n    connectionsByToolkit: composioConnectionsByToolkit,\n    loading: composioLoading,\n    error: composioError,","sourceCodeStart":541,"sourceCodeEnd":577,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/749120085864ce16e0f273c7b86fac7740b39c5b/app/src/pages/Skills.tsx#L541-L577","documentation":"Persisting the newly selected default channel failed in the Skills page handler: after the single-flight guard admitted the click, the persistence write (persist-first, then update UI) rejected, so the stored default-channel preference may disagree with what the UI shows. The busy flag is cleared in the finally, allowing a retry.","triggerScenarios":"Thrown at app/src/pages/Skills.tsx:559 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Check the warned error to identify the failing persist layer (core RPC vs backend)","Verify core/session health and retry the channel selection once the cause is fixed","Confirm the persisted preference after retry rather than trusting the UI toggle","Recurrent failures: inspect the core log for the default-channel config write path"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"749120085864ce16e0f273c7b86fac7740b39c5b","analyzedAt":"2026-08-17T21:21:45.363Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}