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
- Verify the heartbeat run row exists: SELECT * FROM heartbeat_runs WHERE id = <run.id> AND company_id = <companyId> AND agent_id = <agentId>.
- Check that the caller (seedPreparedChatRecovery / native / resumedNative recovery) passes run id, companyId, and agentId from the same originating row, not remapped values.
- If the run was legitimately deleted, skip or recreate the run instead of retrying recovery for the stale reference.
- 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
- Always source run.id/companyId/agentId from the same fetched row, never from mixed inputs.
- Treat run references as ephemeral: revalidate existence before recovery after restarts or DB resets.
- Log the run id on failure so stale references are diagnosable.
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
- question_response_delivery_claim_unavailable
- ACPX provider ownership admission is closed
- ACPX runtime host already has an active turn
- Bridge envelope changed while reading.
- Bridge host response body capacity is busy.
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)