different-ai/openwork · error
saved_workflow_manager_context_required
saved_workflow_manager_context_required
Error message
saved_workflow_manager_context_required
What it means
Saving a workflow under a name that already has a linked active config object creates a new immutable version of that workflow instead of a new workflow. Because replacing another manager's executable code must never happen implicitly, saveWorkflow requires an actor context (input.context) to check the caller holds the 'manager' role on the existing config object; without any context it throws 'saved_workflow_manager_context_required'.
Source
Thrown at ee/apps/den-api/src/workflows.ts:721
}).where(eq(PluginTable.id, plugin.id))
}
const linked = await tx.select({ object: ConfigObjectTable }).from(PluginConfigObjectTable)
.innerJoin(ConfigObjectTable, eq(ConfigObjectTable.id, PluginConfigObjectTable.configObjectId))
.where(and(
eq(PluginConfigObjectTable.pluginId, pluginId),
isNull(PluginConfigObjectTable.removedAt),
eq(ConfigObjectTable.title, input.workflow.name),
inArray(ConfigObjectTable.objectType, ["script", "workflow"]),
eq(ConfigObjectTable.status, "active"),
isNull(ConfigObjectTable.deletedAt),
)).limit(1).for("update")
const configObjectId = linked[0]?.object.id ?? createDenTypeId("configObject")
if (linked[0]) {
// Saving the same name creates a new immutable version of the existing
// Workflow. Plugin edit access can widen the audience, but it must never
// grant authority to replace another Workflow manager's executable code.
if (!input.context) throw new Error("saved_workflow_manager_context_required")
await requirePluginArchResourceRole({
context: input.context,
resourceId: configObjectId,
resourceKind: "config_object",
role: "manager",
})
} else {
await tx.insert(ConfigObjectTable).values({
id: configObjectId,
organizationId,
objectType: "workflow",
sourceMode: "cloud",
title: input.workflow.name,
description: input.workflow.description?.trim() || null,
searchText: `${input.workflow.name} ${input.workflow.description ?? ""}`.trim(),
currentFileName: `${input.workflow.name}.js`,
currentFileExtension: "js",
status: "active",View on GitHub (pinned to 2b7df46e8a)
Solutions
- Pass an actor context whose member holds the manager role on the existing config object
- Rename the workflow to a non-colliding title so a brand-new config object is created instead
- If you should not manage the existing workflow, choose a distinct name rather than overwriting
- For programmatic callers, load the PluginArchActorContext for the owning member before saving
Example fix
// before
await saveWorkflow({ organizationId, ownerMemberId, workflow: { name: existingName, ... }, buildTools }) // contextless overwrite
// after
await saveWorkflow({ organizationId, ownerMemberId, workflow: { name: 'my-report-v2', ... }, buildTools, context: actorContextWithManagerRole }) Defensive patterns
Strategy: validation
Validate before calling
async function collidesWithExistingWorkflow(name: string, pluginId: string) {
const rows = await db.select({ id: ConfigObjectTable.id }).from(PluginConfigObjectTable)
.innerJoin(ConfigObjectTable, eq(ConfigObjectTable.id, PluginConfigObjectTable.configObjectId))
.where(and(
eq(PluginConfigObjectTable.pluginId, pluginId),
isNull(PluginConfigObjectTable.removedAt),
eq(ConfigObjectTable.title, name),
inArray(ConfigObjectTable.objectType, ['script', 'workflow']),
eq(ConfigObjectTable.status, 'active'),
isNull(ConfigObjectTable.deletedAt),
)).limit(1)
return rows.length > 0 // if true, an actor context with the manager role is required
} Type guard
null
Try / catch
try {
await saveWorkflow(input)
} catch (err) {
if (err instanceof Error && err.message === 'saved_workflow_manager_context_required') {
console.error('Name collides with an existing workflow — supply a manager-role context or pick a new name')
}
} Prevention
- Check for title collisions in the UI before submitting a save without context
- Always attach an actor context when overwriting an existing workflow by name
- Delegate the manager-role check to requirePluginArchResourceRole rather than reimplementing it
- Name new workflows uniquely (e.g. include a version suffix) to avoid the overwrite path
When it happens
Trigger: Calling saveWorkflow without input.context while input.workflow.name matches the title of an existing active script/workflow config object linked to the (resolved) plugin — the overwrite-versioning path at line 717.
Common situations: An unauthenticated/system caller re-saving a workflow whose name collides with an existing one; renaming a workflow to a name another manager already uses; background jobs omitting context that worked before only because names were unique.
Related errors
- saved_workflow_plugin_context_required
- workflow_recent_receipt_required
- saved_workflow_plugin_not_found
- invalid_mcp_connection_payload
- invalid_marketplace_payload
AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01).
Data as JSON: /api/errors/5d408d1cdf4da499.
Report an issue: GitHub.