paperclipai/paperclip · error · Error

native_session_bootstrap_identity_conflict

native_session_bootstrap_identity_conflict

Error message

native_session_bootstrap_identity_conflict

What it means

nativeSessionIdForBootstrapPersistence guards the identity of a native (harness-managed) session id when persisting bootstrap state for a run. A run's nativeSessionId is immutable once the native execution has actually started (the runner profile records nativeExecutionInput or provider events exist); it may only rotate while the bootstrap is provably unused. This error means the code tried to assign a different session id to a run whose native identity is already committed.

Solutions

  1. Reuse the run's existing nativeSessionId instead of the newly selected one — only rotate before first use.
  2. Check heartbeat_run_events for the run (provider.event, session.*) to confirm whether the session was already used before choosing to rotate.
  3. Create a fresh run for the new session id rather than re-binding an existing run.
  4. Inspect run.runnerProfileJson.nativeExecutionInput: if present, the binding is immutable and selectedSessionId must equal the persisted value.

Example fix

// before
await prepareNativeSessionBootstrapPersistence(db, { run, selectedSessionId: newSessionId, ... });
// after
const selectedSessionId = run.nativeSessionId ?? newSessionId;
await prepareNativeSessionBootstrapPersistence(db, { run, selectedSessionId, ... });
Defensive patterns

Strategy: type-guard

Validate before calling

function canRotateSessionId(run: { nativeSessionId: string | null; runnerProfileJson: unknown }, hasProviderEvents: boolean, selectedSessionId: string) {
  if (run.nativeSessionId === selectedSessionId) return true;
  const profile = record(run.runnerProfileJson);
  return profile.nativeExecutionInput === undefined && isUnusedNativeSessionBootstrap(run, hasProviderEvents);
}

Type guard

const canRotate = (run, selectedSessionId, hasProviderEvents) => run.nativeSessionId === selectedSessionId || (record(run.runnerProfileJson).nativeExecutionInput === undefined && isUnusedNativeSessionBootstrap(run, hasProviderEvents));

Try / catch

try {
  const sessionId = await prepareNativeSessionBootstrapPersistence(db, input);
} catch (e) {
  if (e.message === "native_session_bootstrap_identity_conflict") {
    // fall back to the run's existing native session id
    return input.run.nativeSessionId;
  }
  throw e;
}

Prevention

When it happens

Trigger: Called via prepareNativeSessionBootstrapPersistence when input.run.nativeSessionId !== input.selectedSessionId AND either (a) record(run.runnerProfileJson).nativeExecutionInput is defined (execution input already persisted), or (b) isUnusedNativeSessionBootstrap(run, hasProviderEvents) returns false (run already emitted provider/session events or is otherwise no longer an unused bootstrap).

Common situations: Re-running bootstrap with a fresh harness-generated session id against a run that already started; a concurrent resume run winning the race for the same native session; retrying bootstrap after a crash that already emitted provider events; passing the wrong run row into the persistence step.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18). Data as JSON: /api/errors/acdaf62a81eed6fd. Report an issue: GitHub.

Appendix: source

Thrown at server/src/services/native-runtime/native-session-resume.ts:257

      db,
      runs.map((run) => run.id),
    ),
  });
}

/** A preassigned session id may rotate only before immutable native admission. */
export function nativeSessionIdForBootstrapPersistence(input: {
  run: NativeSessionBootstrapState & { nativeSessionId: string | null };
  selectedSessionId: string;
  hasProviderEvents: boolean;
}): string {
  if (input.run.nativeSessionId === input.selectedSessionId)
    return input.selectedSessionId;
  if (
    record(input.run.runnerProfileJson).nativeExecutionInput !== undefined ||
    !isUnusedNativeSessionBootstrap(input.run, input.hasProviderEvents)
  ) {
    throw new Error("native_session_bootstrap_identity_conflict");
  }
  return input.selectedSessionId;
}

/** Caller holds the run row lock; recheck authority after waiting for that lock. */
export async function prepareNativeSessionBootstrapPersistence(
  db: Pick<Db, "select">,
  input: {
    run: NativeSessionBootstrapState & {
      id: string;
      nativeSessionId: string | null;
    };
    selectedSessionId: string;
    execution: NativeExecutionInput;
    restoringCheckpoint: boolean;
  },
) {
  const hasProviderEvents = (

View on GitHub (pinned to 3f1d897a7c)