{"record":{"id":"694ff63dd3ed4498","repo":"OpenHands/OpenHands","slug":"branching-a-conversation-isn-t-supported-on-the-cl","errorCode":null,"errorMessage":"Branching a conversation isn't supported on the cloud backend yet.","messagePattern":"Branching a conversation isn't supported on the cloud backend yet\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"src/api/conversation-service/agent-server-conversation-service.api.ts","lineNumber":773,"sourceCode":"    });\n    const [conversation] = await this.batchGetAppConversations([\n      conversationId,\n    ]);\n    return requireAppConversation(conversation, conversationId);\n  }\n\n  /**\n   * Forks a conversation, copying event history up to and including\n   * `fromEventId`. Local agent-server only; needs agent-server >= 1.31.0 for\n   * `from_event_id` (older backends copy the whole conversation).\n   */\n  static async forkConversation(\n    sourceConversationId: string,\n    fromEventId: string,\n    title?: string,\n  ): Promise<DirectConversationInfo> {\n    if (getActiveBackend().backend.kind === \"cloud\") {\n      throw new Error(\n        \"Branching a conversation isn't supported on the cloud backend yet.\",\n      );\n    }\n\n    // `from_event_id` is accepted by `/fork` but not yet typed in\n    // ForkConversationRequest (through client 1.32.0); the client forwards the\n    // body verbatim, so cast to carry it. A title also suppresses the backend\n    // auto-title, so the \"(branch)\" marker sticks.\n    const data = await new ConversationClient(\n      getAgentServerClientOptions(),\n    ).forkConversation<DirectConversationInfo>(sourceConversationId, {\n      from_event_id: fromEventId,\n      ...(title ? { title } : {}),\n    } as ForkConversationRequest & { from_event_id: string });\n\n    // Carry over the source's client-side metadata (repo/branch/workspace/\n    // profile/plugins) so the fork hydrates its chat-page badges the same way.\n    const sourceMetadata = getStoredConversationMetadata(sourceConversationId);","sourceCodeStart":755,"sourceCodeEnd":791,"githubUrl":"https://github.com/OpenHands/OpenHands/blob/500b4c533e9393e44cb92894bcbb18438ef473b6/src/api/conversation-service/agent-server-conversation-service.api.ts#L755-L791","documentation":"Thrown by forkConversation when the active backend is cloud. Conversation forking/branching (POST /fork with from_event_id) is a local agent-server feature only — the cloud backend's app-server does not implement the fork endpoint. This is a deliberate feature gap, not a bug; the error message is user-facing.","triggerScenarios":"AgentServerConversationService.forkConversation(sourceId, eventId, title) is called when getActiveBackend().backend.kind is 'cloud'. The UI branch action triggers this code path on a cloud conversation.","commonSituations":"User clicks 'Branch from here' on a cloud conversation; the branch UI control is not properly hidden for cloud backends; user switched from local to cloud with a branch action queued.","solutions":["Hide or disable the 'Branch from here' UI control when the active backend is cloud.","Show a tooltip explaining branching is not yet available on cloud when the control is visible but disabled.","If branching support is needed on cloud, implement it via the cloud app-server API (requires backend work beyond the frontend)."],"exampleFix":"// before: branch button always shown\n<Button onClick={() => forkConversation(id, eventId)}>Branch from here</Button>\n\n// after: hide for cloud\n{getActiveBackend().backend.kind === 'local' && (\n  <Button onClick={() => forkConversation(id, eventId)}>Branch from here</Button>\n)}","handlingStrategy":"validation","validationCode":"import { getActiveBackend } from '#/api/backend-registry/active-store';\n\nfunction canForkConversation(): boolean {\n  return getActiveBackend().backend.kind === 'local';\n}\n\n// Before showing branch UI:\nif (!canForkConversation()) {\n  hideBranchButton(); // or show with disabled + tooltip\n}","typeGuard":"function isForkNotSupported(e: unknown): boolean {\n  return e instanceof Error && e.message.includes(\"isn't supported on the cloud backend\");\n}","tryCatchPattern":"try {\n  await AgentServerConversationService.forkConversation(sourceId, eventId, title);\n} catch (error) {\n  if (error instanceof Error && error.message.includes('cloud backend')) {\n    showToast('Branching is not available on cloud yet');\n    return;\n  }\n  throw error;\n}","preventionTips":["Hide or disable the 'Branch from here' control when the active backend is cloud.","Show a tooltip explaining the feature gap when the control is visible-but-disabled.","Re-evaluate the backend kind when the user opens the event context menu."],"tags":["cloud","forking","feature-gating","backend-kind"],"backgroundTag":null,"analyzedSha":"500b4c533e9393e44cb92894bcbb18438ef473b6","analyzedAt":"2026-08-12T10:07:46.034Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}