deepseek-ai/deepseek-harness · error · SessionCreateError

session create failed: ${rpcError.code}: ${rpcError.message}

Error message

session create failed: ${rpcError.code}: ${rpcError.message}

What it means

sessions.create() forwards the create request over RPC and the host answered with a failed result; the client wraps the RpcError (code and message) in a SessionCreateError that also carries the requested sessionId when one was preallocated. Reaching this error means a host-side rejection came back as a Result, not a transport throw — the embedded code says why (unknown workspace, inaccessible cwd, duplicate id, host refusal). On success the same path refreshes the project list, so a failed create also means that refresh did not happen.

Source

Thrown at packages/client/runtime/src/client/sessions/service.ts:487

  /** Drop generation-scoped live interaction state the moment a connection generation dies. */
  handleDisconnected(): void {
    this.manager.handleDisconnected()
  }

  /**
   * Create a session on the host. Resolution guarantee: by the time the
   * promise resolves, the created session is in the list store and
   * {@link SessionRuntime.binding} resolves it — callers (New Session
   * draft hand-off) may address the scope synchronously, without waiting a
   * notifier flush. The synchronous projection below makes this structural
   * rather than an accident of microtask ordering.
   * @param opts - target workspace or directory and an optional preallocated id.
   * @returns the new session id.
   * @throws {SessionCreateError} with the requested id.
   */
  async create(opts: { workspaceId?: WorkspaceId; cwd?: string; sessionId?: SessionId } = {}): Promise<SessionId> {
    const result = await this.manager.create(opts)
    if (!result.ok) throw new SessionCreateError(result.error, opts.sessionId)
    this.projectList()
    return result.value.sessionId
  }

  /**
   * Fork a session from a completed-turn prefix of the source (same
   * synchronous-addressability guarantee as {@link SessionRuntime.create}:
   * on resolution the child is in the list store and open() can target it).
   * @param opts - source session id, the optional event seq anchoring the
   *   cut (the boundary is the first turn/end at or after it; an in-log
   *   anchor in an open turn is unavailable rather than clipped backward),
   *   and whether to increment an inherited durable title before resolving.
   *   A fractional anchor floors to a real event seq: the frozen nodes of an
   *   interrupted turn carry flow-ordering seqs between two events, and the
   *   wire takes integers only.
   * @returns the child session id.
   * @throws {SessionForkError} with the source id.
   * @throws {Error} when a requested child-title rename fails after creation.

View on GitHub (pinned to b150a551b8)

Solutions

  1. Read the embedded RpcError code and message off the SessionCreateError to classify the refusal before acting
  2. Refresh the workspace/project list and pass a workspaceId taken from that fresh list
  3. Drop or re-mint the preallocated sessionId when the code indicates an id collision
  4. Verify the cwd exists host-side and the connection is healthy, then retry once at most

Example fix

// before
const id = await sessions.create({ workspaceId, sessionId })

// after — classify the host refusal instead of losing it
try {
  const id = await sessions.create({ workspaceId, sessionId })
} catch (error) {
  if (error instanceof SessionCreateError) {
    // error.message reads 'session create failed: <code>: <message>'; the requested id travels on the error
    throw new Error(`cannot create session in workspace ${workspaceId}: ${error.message}`)
  }
  throw error
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
  return await sessions.create(opts)
} catch (error) {
  if (error instanceof SessionCreateError) {
    // inspect the embedded '<code>: <message>' text and the carried requested id;
    // retry once only for transient classes, with a fresh preallocated id
  }
  throw error
}

Prevention

When it happens

Trigger: await sessions.create({ workspaceId, cwd, sessionId }) with a workspaceId absent from the host's current workspace set, a cwd the host cannot access, a sessionId that already exists host-side, or any other host refusal returned as a failed Result from manager.create().

Common situations: Using a workspace id captured before the host's workspace list changed; passing a local path that does not exist host-side; retrying a create with the same preallocated id after an earlier partial attempt; host permission or policy changes between listing and creating.

Related errors


AI-assisted analysis of deepseek-ai/deepseek-harness@b150a551b8 (2026-08-24). Data as JSON: /api/errors/ef2b0e44c91144cd. Report an issue: GitHub.