paperclipai/paperclip · error · Error

native_runtime_run_missing

native_runtime_run_missing

Error message

native_runtime_run_missing

What it means

prepareNativeHeartbeatRun locks the heartbeat run row (SELECT ... FOR UPDATE) scoped by id, companyId, and agentId before preparing a native runtime run. If no row matches all three predicates, it throws 'native_runtime_run_missing'. This guards against preparing a run that was deleted, belongs to a different company/agent, or was never created.

Solutions

  1. Verify the heartbeat run row exists: SELECT * FROM heartbeat_runs WHERE id = <run.id> AND company_id = <companyId> AND agent_id = <agentId>.
  2. Check that the caller (seedPreparedChatRecovery / native / resumedNative recovery) passes run id, companyId, and agentId from the same originating row, not remapped values.
  3. If the run was legitimately deleted, skip or recreate the run instead of retrying recovery for the stale reference.
  4. After a dev DB reset (rm -rf data/pglite), discard persisted run references from before the reset.

Example fix

// before
await prepareNativeHeartbeatRun({ run: { id: staleRunId, companyId: otherCompany, agentId } });
// after
const [row] = await db.select().from(heartbeatRuns).where(eq(heartbeatRuns.id, runId)).limit(1);
if (!row) return; // run no longer exists; skip recovery
await prepareNativeHeartbeatRun({ run: { id: row.id, companyId: row.companyId, agentId: row.agentId } });
Defensive patterns

Strategy: validation

Validate before calling

const exists = await db.select({ id: heartbeatRuns.id }).from(heartbeatRuns).where(and(eq(heartbeatRuns.id, run.id), eq(heartbeatRuns.companyId, run.companyId), eq(heartbeatRuns.agentId, run.agentId))).limit(1);
if (exists.length === 0) throw new SkipRecoveryError(run.id);

Type guard

function runRowMatches(row, run) { return !!row && row.id === run.id && row.companyId === run.companyId && row.agentId === run.agentId; }

Try / catch

try {
  await prepareNativeHeartbeatRun({ run });
} catch (e) {
  if (e.message === "native_runtime_run_missing") return; // skip stale run
  throw e;
}

Prevention

When it happens

Trigger: Calling prepareNativeHeartbeatRun with input.run.id that does not exist, or where the run's companyId or agentId does not match input.run.companyId/input.run.agentId. Reached via seedPreparedChatRecovery, the native recovery path, or resumedNative recovery.

Common situations: Stale run reference passed into recovery after the run row was deleted or a DB reset; cross-company or cross-agent run IDs supplied by buggy seeding code; retried recovery for a run already purged.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at server/src/services/native-runtime/prepare-native-run.ts:110

      db: input.db,
      companyId: input.run.companyId,
      issue: input.issue,
      actorId: input.run.agentId,
    });
  const binding = contractBinding(completion.contract, completion.row.revision);

  await input.db.transaction(async (tx) => {
    const [locked] = await tx
      .select()
      .from(heartbeatRuns)
      .where(and(
        eq(heartbeatRuns.id, input.run.id),
        eq(heartbeatRuns.companyId, input.run.companyId),
        eq(heartbeatRuns.agentId, input.run.agentId),
      ))
      .for("update")
      .limit(1);
    if (!locked) throw new Error("native_runtime_run_missing");
    if (locked.runtimeModeResolvedAt && locked.runtimeMode !== "native") {
      throw new Error("native_runtime_mode_conflict");
    }
    if (
      locked.runtimeModeResolvedAt
      && (
        locked.runnerInstanceId !== runnerInstanceId
        || locked.nativeSessionId !== normalizedSessionId
        || locked.nativeIssueId !== input.issue.id
        || locked.completionContractId !== completion.row.id
      )
    ) {
      throw new Error("native_runtime_binding_conflict");
    }
    await tx
      .update(heartbeatRuns)
      .set({
        runtimeMode: "native",

View on GitHub (pinned to 3f1d897a7c)