{"record":{"id":"b55435f671c461f5","repo":"OpenHands/OpenHands","slug":"public-sharing-requires-a-cloud-backend","errorCode":null,"errorMessage":"Public sharing requires a cloud backend.","messagePattern":"Public sharing requires a cloud backend\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"src/api/conversation-service/agent-server-conversation-service.api.ts","lineNumber":587,"sourceCode":"    if (getActiveBackend().backend.kind === \"cloud\") {\n      return batchGetCloudConversations(ids);\n    }\n\n    const data = await new ConversationClient(\n      getAgentServerClientOptions(),\n    ).getConversations<DirectConversationInfo>(ids);\n\n    return requireDirectConversationItems(data).map((item) =>\n      toAppConversation(item),\n    );\n  }\n\n  static async updateConversationPublicFlag(\n    conversationId: string,\n    isPublic: boolean,\n  ): Promise<AppConversation> {\n    if (getActiveBackend().backend.kind !== \"cloud\") {\n      throw new Error(\"Public sharing requires a cloud backend.\");\n    }\n    return updateCloudConversationPublicFlag(conversationId, isPublic);\n  }\n\n  static async updateConversationRepository(\n    conversationId: string,\n    repository: string | null,\n    branch?: string | null,\n    gitProvider?: string | null,\n  ): Promise<AppConversation> {\n    if (repository) {\n      const existing = getStoredConversationMetadata(conversationId);\n      setStoredConversationMetadata(conversationId, {\n        ...(existing ?? {}),\n        selected_repository: repository,\n        selected_branch: branch ?? null,\n        git_provider: (gitProvider as Provider | null | undefined) ?? null,\n      });","sourceCodeStart":569,"sourceCodeEnd":605,"githubUrl":"https://github.com/OpenHands/OpenHands/blob/500b4c533e9393e44cb92894bcbb18438ef473b6/src/api/conversation-service/agent-server-conversation-service.api.ts#L569-L605","documentation":"Thrown by updateConversationPublicFlag when the active backend is not cloud. Public sharing (shareable conversation links) is a cloud-only feature backed by the cloud API's updateCloudConversationPublicFlag endpoint. Local agent-server conversations have no concept of public sharing, so the method explicitly rejects non-cloud backends.","triggerScenarios":"The UI calls AgentServerConversationService.updateConversationPublicFlag(conversationId, true) when getActiveBackend().backend.kind is 'local' instead of 'cloud'. This happens if the public-share toggle is visible/enabled on a local backend conversation, or if the user switched from cloud to local while the share dialog was open.","commonSituations":"UI bug where the share button is shown for local conversations; user switched backends (cloud -> local) while a cloud conversation's share modal was open; the share feature was enabled without checking backend kind in the component.","solutions":["Hide the public-share UI control when the active backend is local (check getActiveBackend().backend.kind in the component).","If the user switched backends, close any open share dialogs and reset the share state.","Ensure the feature flag or UI condition for showing the share toggle includes a backend.kind === 'cloud' check."],"exampleFix":"// before: share toggle visible regardless of backend\n{showShareButton && <ShareToggle onChange={...} />}\n\n// after: gate on cloud backend\n{showShareButton && getActiveBackend().backend.kind === 'cloud' && <ShareToggle onChange={...} />}","handlingStrategy":"validation","validationCode":"import { getActiveBackend } from '#/api/backend-registry/active-store';\n\nfunction canShareConversation(): boolean {\n  return getActiveBackend().backend.kind === 'cloud';\n}\n\n// Before showing share UI:\nif (!canShareConversation()) {\n  hideShareButton();\n}","typeGuard":"function isPublicShareRequiresCloud(e: unknown): boolean {\n  return e instanceof Error && e.message === 'Public sharing requires a cloud backend.';\n}","tryCatchPattern":"try {\n  await AgentServerConversationService.updateConversationPublicFlag(id, isPublic);\n} catch (error) {\n  if (error instanceof Error && error.message.includes('requires a cloud backend')) {\n    showToast('Public sharing is only available with a cloud backend');\n    return;\n  }\n  throw error;\n}","preventionTips":["Gate the share/public-toggle UI on getActiveBackend().backend.kind === 'cloud'.","Close share dialogs when switching from cloud to local backend.","Add a feature-flag check alongside the backend-kind check for defense in depth."],"tags":["cloud","public-sharing","feature-gating","backend-kind"],"backgroundTag":null,"analyzedSha":"500b4c533e9393e44cb92894bcbb18438ef473b6","analyzedAt":"2026-08-12T10:07:46.034Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}