{"record":{"id":"a1260152241a449b","repo":"paperclipai/paperclip","slug":"native-continuation-cancellation-run-missing","errorCode":"native_continuation_cancellation_run_missing","errorMessage":"native_continuation_cancellation_run_missing","messagePattern":"native_continuation_cancellation_run_missing","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/src/services/native-runtime/status-decision-committer.ts","lineNumber":705,"sourceCode":"        eq(issueThreadInteractions.issueId, input.issue.id),\n        eq(issueThreadInteractions.status, \"pending\"),\n      ));\n    if (interactionRows.length > 0) {\n      await input.tx.update(issueThreadInteractions).set({\n        status: \"cancelled\",\n        resolvedAt: new Date(),\n        updatedAt: new Date(),\n      }).where(inArray(issueThreadInteractions.id, interactionRows.map((row) => row.id)));\n    }\n    const [run] = await input.tx.update(heartbeatRuns).set({\n      status: \"cancelled\",\n      finishedAt: new Date(),\n      updatedAt: new Date(),\n    }).where(and(\n      eq(heartbeatRuns.id, input.runId),\n      eq(heartbeatRuns.companyId, input.companyId),\n    )).returning();\n    if (!run) throw new Error(\"native_continuation_cancellation_run_missing\");\n    input.terminalRunsToEmit?.push(run);\n    return {\n      effectKind: effect.kind,\n      targetType: \"heartbeat_run\",\n      targetId: run.id,\n      payload: {\n        cancelledWakeIds: wakeRows.map((row) => row.id),\n        cancelledInteractionIds: interactionRows.map((row) => row.id),\n      },\n    };\n  }\n  if (effect.kind === \"append_superseding_assessment\") {\n    const lineage = await input.tx.select({\n      currentAssessmentId: statusDecisions.assessmentId,\n    }).from(statusDecisions).where(and(\n      eq(statusDecisions.id, input.decisionId),\n      eq(statusDecisions.companyId, input.companyId),\n    )).limit(1).then((rows) => rows[0] ?? null);","sourceCodeStart":687,"sourceCodeEnd":723,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/server/src/services/native-runtime/status-decision-committer.ts#L687-L723","documentation":"In materializeDecisionEffect (server/src/services/native-runtime/status-decision-committer.ts:705), the `cancel_continuations` effect attempts to cancel a heartbeat run with `UPDATE heartbeat_runs SET status='cancelled' ... WHERE id = input.runId AND company_id = input.companyId RETURNING *`. If no row is returned, the run either does not exist or belongs to a different company, and this sentinel error is thrown to abort the commit transaction. It is a company-scoped entity-not-found guard: the committer refuses to fabricate a cancellation for a run it cannot see.","triggerScenarios":"Committing a status decision whose effect kind is `cancel_continuations` while input.runId references a heartbeat_runs row that is absent, already hard-deleted, or scoped to a different companyId than input.companyId.","commonSituations":"Stale runId captured before the run was purged/migrated; passing the wrong run id (e.g. issue id or taskId) into the committer; cross-company data leak attempt or mis-scoped caller context; idempotent re-commit after the run row was already finalized and removed.","solutions":["Verify input.runId is a valid heartbeat_runs id for the same company before materializing the cancel_continuations effect; log and inspect the decision payload.","Query heartbeat_runs by id/companyId first; if the run is already terminal or missing, treat the cancellation as a no-op instead of throwing (add an idempotency guard).","Fix the caller that produced the stale/mismatched runId so decisions always reference a live, company-scoped run.","If rows were intentionally deleted, relax the update's WHERE or use a softer lookup that tolerates missing runs for already-terminal decisions."],"exampleFix":"// before\nconst [run] = await tx.update(heartbeatRuns).set({...}).where(and(\n  eq(heartbeatRuns.id, input.runId),\n  eq(heartbeatRuns.companyId, input.companyId),\n)).returning();\nif (!run) throw new Error(\"native_continuation_cancellation_run_missing\");\n// after\nconst [run] = await tx.update(heartbeatRuns).set({...}).where(and(\n  eq(heartbeatRuns.id, input.runId),\n  eq(heartbeatRuns.companyId, input.companyId),\n)).returning();\nif (!run) {\n  const existing = await tx.select({ id: heartbeatRuns.id }).from(heartbeatRuns)\n    .where(and(eq(heartbeatRuns.id, input.runId), eq(heartbeatRuns.companyId, input.companyId)));\n  if (existing.length === 0) return { effectKind: effect.kind, targetType: \"heartbeat_run\", targetId: input.runId, payload: { alreadyGone: true } };\n  throw new Error(\"native_continuation_cancellation_run_missing\"); // exists but not terminal-able\n}","handlingStrategy":"validation","validationCode":"const existing = await db.select({ id: heartbeatRuns.id, companyId: heartbeatRuns.companyId })\n  .from(heartbeatRuns)\n  .where(and(eq(heartbeatRuns.id, runId), eq(heartbeatRuns.companyId, companyId)));\nif (existing.length === 0) {\n  // skip cancellation: run missing or cross-company — don't materialize the effect\n}","typeGuard":"function isCancelableRun(\n  run: { id: string; companyId: string; status: string } | undefined,\n  expectedCompanyId: string,\n): run is { id: string; companyId: string; status: string } {\n  return !!run && run.companyId === expectedCompanyId;\n}","tryCatchPattern":"try {\n  await committer.commit(decision);\n} catch (err) {\n  if (err instanceof Error && err.message === \"native_continuation_cancellation_run_missing\") {\n    logger.warn({ runId }, \"cancel_continuations target run missing; treating as no-op\");\n    return;\n  }\n  throw err;\n}","preventionTips":["Always source runId from the same company-scoped query/decision that produced it; never pass ids across company contexts.","Pre-validate run existence and terminal status before enqueuing a cancel_continuations effect.","Make the cancellation idempotent: a missing or already-terminal run should be a no-op, not a hard failure.","Add an assertion/log when a decision references a runId that no longer resolves, to catch purge/migration bugs."],"tags":["database","entity-not-found","company-scoping","native-runtime"],"backgroundTag":"record-not-found","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}