paperclipai/paperclip · error · WatchdogDecisionApplicationError
run_not_found
run_not_found
Error message
Heartbeat run not found
What it means
createRecordWatchdogDecision looks up the heartbeat run with reader.findRunForCompany(companyId, runId); when no run matches — including runs that exist but belong to a different company — it throws WatchdogDecisionApplicationError with code run_not_found. This is a company-scoped not-found guard for recording watchdog decisions on active-run (heartbeat) runs.
Source
Thrown at server/src/modules/active-run-watchdog/application/use-cases.ts:137
export type RecordWatchdogDecisionUseCaseInput = {
companyId: string;
runId: string;
actor: WatchdogDecisionActor;
decision: "snooze" | "continue" | "dismissed_false_positive";
evaluationIssueId?: string | null;
reason?: string | null;
snoozedUntil?: Date | null;
createdByRunId?: string | null;
now?: Date;
};
export function createRecordWatchdogDecision(deps: RecordWatchdogDecisionDeps) {
return async function recordWatchdogDecision(
input: RecordWatchdogDecisionUseCaseInput,
): Promise<WatchdogDecisionRecord> {
const run = await deps.reader.findRunForCompany(input.companyId, input.runId);
if (!run) throw new WatchdogDecisionApplicationError("run_not_found", "Heartbeat run not found");
const evaluationIssue = input.evaluationIssueId
? await deps.reader.findEvaluationIssueById(input.companyId, input.evaluationIssueId)
: null;
if (input.evaluationIssueId && !evaluationIssue) {
throw new WatchdogDecisionApplicationError("evaluation_issue_not_found", "Evaluation issue not found");
}
if (input.actor.type === "agent" && !evaluationIssue) {
throw new WatchdogDecisionApplicationError(
"evaluation_issue_required",
"Agent watchdog decisions require the target evaluation issue",
);
}
const boardActor = input.actor.type === "board";
const assignedRecoveryOwner =
input.actor.type === "agent" &&
Boolean(input.actor.agentId) &&View on GitHub (pinned to 01ad858492)
Solutions
- Verify the runId exists in the heartbeat runs table and belongs to the same companyId in the request context
- Re-fetch the current run list for the company to confirm the ID is still valid before recording a decision
- Check for cross-company context leakage: confirm the authenticated actor's companyId matches the run's company
- If the run was deleted legitimately, treat the decision as obsolete instead of retrying
Example fix
// before
await recordWatchdogDecision({ companyId: otherCompanyId, runId: cachedRunId, ... });
// after
const runs = await listHeartbeatRuns(companyId);
const run = runs.find(r => r.id === runId);
if (!run) throw new Error('run not found in company; refetch before recording decision');
await recordWatchdogDecision({ companyId, runId: run.id, ... }); Defensive patterns
Strategy: validation
Validate before calling
const run = await listHeartbeatRuns(companyId).then(rs => rs.find(r => r.id === runId)); if (!run) throw new Error(`run ${runId} not found for company ${companyId}`); Type guard
null
Try / catch
try { await recordWatchdogDecision(input); } catch (e) { if (e.code === 'run_not_found') { /* refetch run list; abort decision as obsolete */ } else throw e; } Prevention
- Always resolve run IDs from a company-scoped query in the same request context
- Do not cache heartbeat run IDs across company switches or long-lived automation
- Confirm ID integrity end-to-end (no proxy truncation/rewriting)
When it happens
Trigger: Calling recordWatchdogDecision (via the active-run-watchdog API or createActiveRunWatchdog wiring) with a runId that does not exist, was deleted, is mistyped, or exists under a different companyId so the company-scoped query returns nothing.
Common situations: Clients caching run IDs from a different company context; decisions recorded after the heartbeat run was purged; UUID truncated/transformed by a proxy; passing a heartbeat ID vs run ID confusion between tables.
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
- evaluation_issue_not_found
- Unable to resolve company for plugin API route
- Job not found
- Probe found no matching Anthropic Environment
- Probe found no matching Anthropic Agent
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/c2fb93a491936d61.
Report an issue: GitHub.