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
- Reuse the run's existing nativeSessionId instead of the newly selected one — only rotate before first use.
- Check heartbeat_run_events for the run (provider.event, session.*) to confirm whether the session was already used before choosing to rotate.
- Create a fresh run for the new session id rather than re-binding an existing run.
- 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
- Always prefer the run's existing nativeSessionId over a newly selected one.
- Query heartbeat_run_events for provider evidence before deciding to rotate a session id.
- Never reuse a run row across distinct native sessions; create a new run instead.
- Log runnerProfileJson.nativeExecutionInput presence when debugging bootstrap failures.
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
- Photon send identity changed
- runner_prp_session_released
- ACPX runtime executable changed while it was verified
- ACPX executable changed during snapshot
- ACPX file ended during snapshot
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)