paperclipai/paperclip · error · WatchdogDecisionApplicationError

creator_run_invalid

creator_run_invalid

Error message

createdByRunId is not valid for this watchdog decision actor

What it means

WatchdogDecisionApplicationError with code creator_run_invalid, raised when a watchdog decision supplies a createdByRunId (or actor.runId) that does not resolve to a run in the same company, or an agent actor claims a creator run owned by a different agent. It validates the actor's authority to act on behalf of the creator run.

Solutions

  1. Verify the run exists in the same company via GET for the run before submitting the decision
  2. Ensure actor.runId / createdByRunId refers to a run owned by the acting agent
  3. Omit createdByRunId if there is no valid creator run
  4. Re-fetch fresh run identity after restarts instead of reusing persisted ids

Example fix

// before
recordWatchdogDecision({ companyId, actor: { type: 'agent', agentId }, createdByRunId: staleRunId });
// after
const run = await getRun(companyId, runId);
if (run && run.agentId === agentId) {
  recordWatchdogDecision({ companyId, actor: { type: 'agent', agentId }, createdByRunId: run.id });
}
Defensive patterns

Strategy: validation

Validate before calling

const run = await getRun(companyId, createdByRunId);
if (!run || run.companyId !== companyId || run.agentId !== actor.agentId) throw new Error('invalid creator run');

Type guard

const validCreator = (r, actor) => !!r && r.companyId === actor.companyId && (actor.type !== 'agent' || r.agentId === actor.agentId);

Try / catch

try { await recordWatchdogDecision(input) } catch (e) { if (e.code === 'creator_run_invalid') { /* drop or re-attribute decision */ } else throw e; }

Prevention

When it happens

Trigger: Calling recordWatchdogDecision with createdByRunId of a deleted run, a run from another company, or a run whose agentId differs from actor.agentId for an agent actor.

Common situations: Stale run id cached by an agent after the run was deleted; company id mismatch in multi-tenant calls; agent attempting to attribute decisions to another agent's run.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10). Data as JSON: /api/errors/90b306c86b2fe4f6. Report an issue: GitHub.

Appendix: source

Thrown at server/src/modules/active-run-watchdog/application/use-cases.ts:188

      evaluationIssue.originKind !== STALE_ACTIVE_RUN_EVALUATION_ORIGIN_KIND ||
      evaluationIssue.originId !== run.id
    )) {
      throw new WatchdogDecisionApplicationError(
        "evaluation_issue_mismatch",
        "Watchdog decision evaluation issue is not bound to the target run",
      );
    }

    const createdByRunId = input.actor.type === "agent"
      ? input.actor.runId ?? input.createdByRunId ?? null
      : input.actor.type === "board"
        ? input.actor.runId ?? input.createdByRunId ?? null
        : null;
    if (createdByRunId) {
      const creatorRun = await deps.reader.findRunForCompany(input.companyId, createdByRunId);
      const sameAgent = input.actor.type !== "agent" || creatorRun?.agentId === input.actor.agentId;
      if (!creatorRun || !sameAgent) {
        throw new WatchdogDecisionApplicationError(
          "creator_run_invalid",
          "createdByRunId is not valid for this watchdog decision actor",
        );
      }
    }

    const decisionNow = input.now ?? new Date();
    const effectiveSnoozedUntil = input.decision === "snooze"
      ? input.snoozedUntil ?? null
      : input.decision === "continue"
        ? input.snoozedUntil && input.snoozedUntil > decisionNow
          ? input.snoozedUntil
          : new Date(decisionNow.getTime() + deps.continueRearmMs)
        : null;

    return deps.writer.recordDecision(input.companyId, {
      runId: run.id,
      actor: input.actor,

View on GitHub (pinned to 01ad858492)