paperclipai/paperclip · error

native_session_attach_binding_mismatch

native_session_attach_binding_mismatch

Error message

native_session_attach_binding_mismatch

What it means

attachRun verifies that the run identity being attached (sessionId, companyId, issueId, agentId) exactly matches the identity the native session was originally created with. If any of the four fields differ, the backend refuses to attach the run to a session bound to different coordinates and throws this error. It is a safety guard against cross-issue or cross-agent session reuse in the harness driver backend.

Source

Thrown at packages/paperclip-runner/src/backends/harness-driver-backend.ts:476

        this.#session.resolveRuntimeRequest !== undefined,
      runtimeRequestHandoff: this.#session.handoffRuntimeRequest !== undefined,
      goals: this.#session.goal !== undefined,
      threadLineage: this.#session.lineage !== undefined,
    };
  }

  async attachRun(input: {
    identity: OpenNativeSessionInput["identity"];
  }): Promise<void> {
    this.#assertProtocolIntegrity();
    const currentIdentity = this.#input.identity;
    if (
      input.identity.sessionId !== currentIdentity.sessionId ||
      input.identity.companyId !== currentIdentity.companyId ||
      input.identity.issueId !== currentIdentity.issueId ||
      input.identity.agentId !== currentIdentity.agentId
    ) {
      throw new Error("native_session_attach_binding_mismatch");
    }
    if (this.#session.attachRun === undefined) {
      throw new Error("native_session_multi_run_unavailable");
    }
    try {
      await this.#session.attachRun({ runId: input.identity.runId });
      this.#assertProtocolIntegrity();
    } catch (error) {
      this.#rethrowProtocolIntegrity(error);
      throw error;
    }
    this.#input = { ...this.#input, identity: structuredClone(input.identity) };
    this.#terminal = null;
    this.#explicitlyCancelled = false;
  }

  async detachControllerForRestart(): Promise<void> {
    if (this.#session.detachControllerForRestart === undefined) return;

View on GitHub (pinned to 01ad858492)

Solutions

  1. Log both input.identity and the session's currentIdentity and align the mismatched field before calling attachRun
  2. Create a fresh native session for the new identity instead of attaching to the existing one
  3. Ensure the caller derives identity from the same record that bootstrapped the session (single source of truth)
  4. If the identity legitimately changed, detach/tear down the old session and re-initialize with the new binding

Example fix

// before
await backend.attachRun({ identity: { sessionId, companyId: otherCompanyId, issueId, agentId, runId } });
// after
if (currentIdentity.companyId !== companyId) {
  await backend.close();
  backend = await createSession({ sessionId, companyId, issueId, agentId });
}
await backend.attachRun({ identity: { sessionId, companyId, issueId, agentId, runId } });
Defensive patterns

Strategy: validation

Validate before calling

const mismatches = ['sessionId','companyId','issueId','agentId'].filter(k => input.identity[k] !== currentIdentity[k]);
if (mismatches.length > 0) throw new Error(`identity mismatch: ${mismatches.join(',')}`);

Type guard

function isSameIdentity(a, b) {
  return a.sessionId === b.sessionId && a.companyId === b.companyId && a.issueId === b.issueId && a.agentId === b.agentId;
}

Try / catch

try { await backend.attachRun({ identity }); } catch (e) { if (e.message === 'native_session_attach_binding_mismatch') { await recreateSessionForIdentity(identity); } else throw e; }

Prevention

When it happens

Trigger: Calling attachRun with input.identity whose sessionId, companyId, issueId, or agentId does not equal the currentIdentity captured when the native session was started. Typical: reusing a cached session object for a different issue, or a stale session after an issue was re-keyed/re-created.

Common situations: Recovering from a crash by reattaching to a persisted sessionId but with a new issueId; passing the wrong issue's runId to a long-lived session; company/agent rebinding after an admin reassignment; a bug where identity is rebuilt from a different source than the session bootstrap.

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@01ad858492 (2026-09-10). Data as JSON: /api/errors/e701eac5beed029d. Report an issue: GitHub.