deepseek-ai/deepseek-harness · error · SessionForkError
session fork failed: ${rpcError.code}: ${rpcError.message}
Error message
session fork failed: ${rpcError.code}: ${rpcError.message} What it means
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.
Source
Thrown at packages/client/runtime/src/client/sessions/service.ts:522
* @throws {SessionForkError} with the source id.
* @throws {Error} when a requested child-title rename fails after creation.
*/
async fork(opts: {
sessionId: SessionId
atSeq?: number
increaseTitle?: boolean
}): Promise<SessionId> {
const sourceTitle = opts.increaseTitle
? this.list.getSnapshot().byId[opts.sessionId]?.title
: undefined
const result = await this.manager.fork({
sessionId: opts.sessionId,
// Flooring lands inside the anchor's own turn (every turn opens with a
// turn/start), so the host's first-turn/end-at-or-after cut still ends
// on that turn — never clipped back to the previous one.
...(opts.atSeq === undefined ? {} : { atSeq: Math.floor(opts.atSeq) }),
})
if (!result.ok) throw new SessionForkError(result.error, opts.sessionId)
this.projectList()
const childId = result.value.sessionId
if (sourceTitle !== undefined) {
const child = this.binding(childId)?.session
if (child === undefined) throw new Error(`fork child "${childId}" is not locally addressable`)
const renamed = await child.rename(increasedForkTitle(sourceTitle))
if (!renamed.ok) throw new Error(`fork child rename failed: ${renamed.error.code}: ${renamed.error.message}`)
}
return childId
}
/**
* Resolve an Agent-scoped context view (use-and-discard).
* @param id - session id (the agent identity — 1:1 same axis).
* @returns scoped ctx, or undefined for a session neither listed nor already scoped.
*/
scope(id: SessionId): AgentContext | undefined {
return this.resolve(id)?.ctxView on GitHub (pinned to b150a551b8)
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
Example fix
// before
const childId = await sessions.fork(sourceId, { atSeq })
// after — anchor from an observed event, classify failures
const anchor = lastCompletedTurnSeq(source) // an event seq you actually saw on this session
try {
const childId = await sessions.fork(sourceId, { atSeq: anchor })
} catch (error) {
if (error instanceof SessionForkError) {
// message reads 'session fork failed: <code>: <message>'; the requested child id is on the error
}
throw error
} Defensive patterns
Strategy: try-catch
Validate before calling
// guard the two host-rejectable inputs before forking
if (!currentList().some(s => s.sessionId === sourceId)) {
throw new Error(`fork source ${sourceId} is not in the current list`)
}
const anchor = Math.floor(atSeq)
if (anchor > lastObservedSeq(sourceSession)) {
throw new Error('atSeq beyond the source session log')
} Try / catch
try {
childId = await sessions.fork(sourceId, { atSeq })
} catch (error) {
if (error instanceof SessionForkError) {
// read the embedded '<code>: <message>' text; refresh the list before any retry
}
throw error
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- session create failed: ${rpcError.code}: ${rpcError.message}
- fork child rename failed: ${renamed.error.code}: ${renamed.e
- context-not-found
- transport failure for ${channel}/${endpoint}: HTTP ${respons
- rpcId mismatch for ${endpoint}: sent ${rpcId}, got ${full.rp
AI-assisted analysis of deepseek-ai/deepseek-harness@b150a551b8 (2026-08-24).
Data as JSON: /api/errors/1812507d6a443402.
Report an issue: GitHub.