different-ai/openwork · error
workflow_recent_receipt_required
workflow_recent_receipt_required
Error message
workflow_recent_receipt_required
What it means
Saving a workflow requires proof that the exact code being saved has actually run successfully recently in this organization by this member. saveWorkflow queries WorkflowRunTable for a succeeded run with a matching code digest finished within RECENT_RUN_WINDOW_MS; if no such receipt exists it throws 'workflow_recent_receipt_required'. This prevents saving untested or never-executed code as a reusable workflow.
Source
Thrown at ee/apps/den-api/src/workflows.ts:620
) {
throw new Error("saved_workflow_plugin_context_required")
}
await requirePluginArchResourceRole({
context: input.context,
resourceId: requestedPluginId,
resourceKind: "plugin",
role: "editor",
})
}
const receipts = await db.select().from(WorkflowRunTable).where(and(
eq(WorkflowRunTable.organization_id, organizationId),
eq(WorkflowRunTable.org_membership_id, ownerMemberId),
eq(WorkflowRunTable.code_digest, codemodeCodeDigest(input.workflow.code)),
eq(WorkflowRunTable.status, "succeeded"),
gt(WorkflowRunTable.finished_at, new Date(Date.now() - RECENT_RUN_WINDOW_MS)),
)).orderBy(desc(WorkflowRunTable.finished_at)).limit(1)
const receipt = receipts[0]
if (!receipt) throw new Error("workflow_recent_receipt_required")
const built = await input.buildTools()
const manifestByPath = new Map(built.manifest.flatMap((entry) => [
[entry.scriptPath, entry] as const,
[entry.scriptPath.replace(/^tools\./, ""), entry] as const,
]))
const requiredCapabilities: Array<{ capabilityName: string; scriptPath: string }> = []
for (const call of parseCodemodeToolCalls(receipt.tool_calls)) {
const resolved = manifestByPath.get(call.name)
if (!resolved) throw new Error(`workflow_capability_unavailable:${call.name}`)
if (resolved.readOnly !== true) throw new Error(`workflow_requires_read_only_capabilities:${call.name}`)
if (!requiredCapabilities.some((entry) => entry.scriptPath === resolved.scriptPath)) {
requiredCapabilities.push({
capabilityName: resolved.capabilityName,
scriptPath: resolved.scriptPath,
})
}
}View on GitHub (pinned to 2b7df46e8a)
Solutions
- Execute the workflow code successfully once before saving, with the same member and organization
- Confirm the most recent succeeded run is within the recent-run window; re-run if stale
- Make sure the code string is byte-identical to what ran (the digest must match — no whitespace-only edits between run and save)
- Check that the run was recorded under the same ownerMemberId, not another membership
Example fix
// before
const result = await runCodemode(code) // run failed
await saveWorkflow({ workflow: { code, ... } }) // throws workflow_recent_receipt_required
// after
const result = await runCodemode(code)
if (!result.ok) throw new Error('fix code and re-run before saving')
await saveWorkflow({ workflow: { code, ... } }) Defensive patterns
Strategy: validation
Validate before calling
async function hasRecentRunReceipt(code: string, organizationId: string, member: string) {
const rows = await db.select().from(WorkflowRunTable).where(and(
eq(WorkflowRunTable.organization_id, organizationId),
eq(WorkflowRunTable.org_membership_id, member),
eq(WorkflowRunTable.code_digest, codemodeCodeDigest(code)),
eq(WorkflowRunTable.status, 'succeeded'),
gt(WorkflowRunTable.finished_at, new Date(Date.now() - RECENT_RUN_WINDOW_MS)),
)).limit(1)
return rows.length > 0
} Type guard
null
Try / catch
try {
await saveWorkflow(input)
} catch (err) {
if (err instanceof Error && err.message === 'workflow_recent_receipt_required') {
// surface 'run the workflow successfully, then save' to the user
}
} Prevention
- Always run the exact code successfully immediately before saving
- Avoid any edit (even whitespace) between the test run and the save — the digest must match
- Make save-and-run an atomic UI flow rather than two independent actions
- Watch for run/save happening under different member accounts in multi-account setups
When it happens
Trigger: Calling saveWorkflow with workflow.code whose codemodeCodeDigest has no succeeded WorkflowRun row for the same organization_id, org_membership_id, finished within the recent-run window (e.g. the run failed, ran under a different member/org, or is older than the window).
Common situations: Editing code and saving before re-running it in the UI; the test run happened more than RECENT_RUN_WINDOW_MS ago; the run executed under a different member account; a failed or still-running run is the only record.
Related errors
- saved_workflow_plugin_context_required
- workflow_invalid_schema
- saved_workflow_plugin_not_found
- saved_workflow_manager_context_required
- Agent context diagnostics timeout must be between 1 ms and 3
AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01).
Data as JSON: /api/errors/01b4ea0ebc98c33e.
Report an issue: GitHub.