different-ai/openwork · error

automation_owner_inactive

automation_owner_inactive

Error message

automation_owner_inactive

What it means

Thrown in validateWorkflowAutomationAction when the automation's ownerMemberId does not resolve to an active organization member: the MemberTable lookup by id + organizationId + removedAt IS NULL returns nothing. Automations must be owned by a current, non-removed member with sufficient access.

Source

Thrown at ee/apps/den-api/src/workflows.ts:523

}

export async function validateWorkflowAutomationAction(input: {
  organizationId: string
  ownerMemberId: string
  action: Extract<AutomationAction, { kind: "saved_script" }>
}) {
  const organizationId = normalizeDenTypeId("organization", input.organizationId)
  const ownerMemberId = normalizeDenTypeId("member", input.ownerMemberId)
  const pluginId = normalizeDenTypeId("plugin", input.action.script.pluginId)
  const configObjectId = normalizeDenTypeId("configObject", input.action.script.configObjectId)
  const configObjectVersionId = normalizeDenTypeId("configObjectVersion", input.action.script.configObjectVersionId)
  const members = await db.select({ role: MemberTable.role }).from(MemberTable).where(and(
    eq(MemberTable.id, ownerMemberId),
    eq(MemberTable.organizationId, organizationId),
    isNull(MemberTable.removedAt),
  )).limit(1)
  const member = members[0]
  if (!member) throw new Error("automation_owner_inactive")
  if (!memberHasRole(member.role, "admin")) {
    const [teams, configObjectGrants, pluginGrants] = await Promise.all([
      db.select({ id: TeamMemberTable.teamId }).from(TeamMemberTable)
        .where(eq(TeamMemberTable.orgMembershipId, ownerMemberId)),
      db.select({
        orgMembershipId: ConfigObjectAccessGrantTable.orgMembershipId,
        orgWide: ConfigObjectAccessGrantTable.orgWide,
        removedAt: ConfigObjectAccessGrantTable.removedAt,
        role: ConfigObjectAccessGrantTable.role,
        teamId: ConfigObjectAccessGrantTable.teamId,
      }).from(ConfigObjectAccessGrantTable).where(and(
        eq(ConfigObjectAccessGrantTable.organizationId, organizationId),
        eq(ConfigObjectAccessGrantTable.configObjectId, configObjectId),
      )),
      db.select({
        orgMembershipId: PluginAccessGrantTable.orgMembershipId,
        orgWide: PluginAccessGrantTable.orgWide,
        removedAt: PluginAccessGrantTable.removedAt,

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Reassign the automation to an active member of the organization (e.g. an admin).
  2. Restore the removed member's membership if the ownership was correct.
  3. Validate ownerMemberId is an active member before calling create/update.

Example fix

// before
await createAutomation({ ownerMemberId: departedUserId, ... })
// after
await createAutomation({ ownerMemberId: activeAdminMemberId, ... })
Defensive patterns

Strategy: validation

Validate before calling

const [m] = await db.select({ role: MemberTable.role }).from(MemberTable)
  .where(and(eq(MemberTable.id, ownerMemberId), eq(MemberTable.organizationId, organizationId), isNull(MemberTable.removedAt))).limit(1)
const ownerActive = !!m

Type guard

const isActiveMember = (m: {removedAt: Date | null} | undefined): m is {removedAt: null} => m !== undefined && m.removedAt === null

Try / catch

try {
  await updateAutomation({ ownerMemberId, ... })
} catch (e) {
  if (e.message === "automation_owner_inactive") {
    // reassign to an active admin member
  } else throw e
}

Prevention

When it happens

Trigger: Creating or updating a workflow automation whose owner member was removed from the organization, never belonged to it, or whose membership row was soft-deleted (removedAt set).

Common situations: Offboarding a user while their scheduled automations keep running; transferring ownership to an invalid member id; org data migration leaving dangling member references.

Related errors


AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01). Data as JSON: /api/errors/e6e31332eb46890d. Report an issue: GitHub.