{"record":{"id":"1812507d6a443402","repo":"deepseek-ai/deepseek-harness","slug":"session-fork-failed-rpcerror-code-rpcerror","errorCode":null,"errorMessage":"session fork failed: ${rpcError.code}: ${rpcError.message}","messagePattern":"session fork failed: (.+?): (.+?)","errorType":"exception","errorClass":"SessionForkError","httpStatus":null,"severity":"error","filePath":"packages/client/runtime/src/client/sessions/service.ts","lineNumber":522,"sourceCode":"   * @throws {SessionForkError} with the source id.\n   * @throws {Error} when a requested child-title rename fails after creation.\n   */\n  async fork(opts: {\n    sessionId: SessionId\n    atSeq?: number\n    increaseTitle?: boolean\n  }): Promise<SessionId> {\n    const sourceTitle = opts.increaseTitle\n      ? this.list.getSnapshot().byId[opts.sessionId]?.title\n      : undefined\n    const result = await this.manager.fork({\n      sessionId: opts.sessionId,\n      // Flooring lands inside the anchor's own turn (every turn opens with a\n      // turn/start), so the host's first-turn/end-at-or-after cut still ends\n      // on that turn — never clipped back to the previous one.\n      ...(opts.atSeq === undefined ? {} : { atSeq: Math.floor(opts.atSeq) }),\n    })\n    if (!result.ok) throw new SessionForkError(result.error, opts.sessionId)\n    this.projectList()\n    const childId = result.value.sessionId\n    if (sourceTitle !== undefined) {\n      const child = this.binding(childId)?.session\n      if (child === undefined) throw new Error(`fork child \"${childId}\" is not locally addressable`)\n      const renamed = await child.rename(increasedForkTitle(sourceTitle))\n      if (!renamed.ok) throw new Error(`fork child rename failed: ${renamed.error.code}: ${renamed.error.message}`)\n    }\n    return childId\n  }\n\n  /**\n   * Resolve an Agent-scoped context view (use-and-discard).\n   * @param id - session id (the agent identity — 1:1 same axis).\n   * @returns scoped ctx, or undefined for a session neither listed nor already scoped.\n   */\n  scope(id: SessionId): AgentContext | undefined {\n    return this.resolve(id)?.ctx","sourceCodeStart":504,"sourceCodeEnd":540,"githubUrl":"https://github.com/deepseek-ai/deepseek-harness/blob/b150a551b8d465e31e418e1b2eaf5e79bbb7d28e/packages/client/runtime/src/client/sessions/service.ts#L504-L540","documentation":"sessions.fork() asked the host to fork a source session at a completed-turn prefix and the RPC came back failed; the client wraps the RpcError in a SessionForkError carrying the requested child sessionId. The request floors atSeq before sending (Math.floor), so fractional anchors are never the cause — the host rejected the fork itself: unknown or deleted source, an anchor the host cannot cut at, or another host-side refusal.","triggerScenarios":"await sessions.fork(sourceId, { atSeq, sessionId }) where sourceId no longer exists host-side, atSeq points beyond or outside the source log's cuttable range, or the preallocated child sessionId collides with an existing session.","commonSituations":"Forking from a stale session list after the source was deleted; computing atSeq from another session's events or from projected state; racing a session delete; re-forking with the same requested child id after a prior attempt.","solutions":["Confirm the source session still exists in a fresh list before forking","Derive atSeq from an event actually observed on the source session's own log and keep it within that range","Let the host allocate the child id (omit sessionId) unless you need it for recovery","Read the wrapped RpcError code to distinguish source-missing from anchor or permission refusals"],"exampleFix":"// before\nconst childId = await sessions.fork(sourceId, { atSeq })\n\n// after — anchor from an observed event, classify failures\nconst anchor = lastCompletedTurnSeq(source) // an event seq you actually saw on this session\ntry {\n  const childId = await sessions.fork(sourceId, { atSeq: anchor })\n} catch (error) {\n  if (error instanceof SessionForkError) {\n    // message reads 'session fork failed: <code>: <message>'; the requested child id is on the error\n  }\n  throw error\n}","handlingStrategy":"try-catch","validationCode":"// guard the two host-rejectable inputs before forking\nif (!currentList().some(s => s.sessionId === sourceId)) {\n  throw new Error(`fork source ${sourceId} is not in the current list`)\n}\nconst anchor = Math.floor(atSeq)\nif (anchor > lastObservedSeq(sourceSession)) {\n  throw new Error('atSeq beyond the source session log')\n}","typeGuard":null,"tryCatchPattern":"try {\n  childId = await sessions.fork(sourceId, { atSeq })\n} catch (error) {\n  if (error instanceof SessionForkError) {\n    // read the embedded '<code>: <message>' text; refresh the list before any retry\n  }\n  throw error\n}","preventionTips":["Re-list sessions before forking from UI state older than the current list","Take atSeq only from the source session's own observed events","Omit sessionId to let the host allocate unless recovery needs a known id"],"tags":["rpc","session-fork","at-seq","host-error"],"backgroundTag":"rpc-request-failed","analyzedSha":"b150a551b8d465e31e418e1b2eaf5e79bbb7d28e","analyzedAt":"2026-08-24T18:12:29.105Z","schemaVersion":2},"datasetVersion":"2026-08-24T22:17:12.610Z"}