different-ai/openwork · error · PluginArchRouteFailure

member_not_joined

member_not_joined

Error message

Connector creator member has not joined the organization.

What it means

Thrown when the connector creator's member row exists and is active, but `member.userId` is null — i.e. the membership record has not yet been claimed by a user who actually joined (completed sign-up/invitation acceptance). The route cannot build an actor context without a user id, so it fails with 404 member_not_joined.

Source

Thrown at ee/apps/den-api/src/routes/org/plugin-system/store.ts:5875

    throw new PluginArchRouteFailure(404, "organization_not_found", "Organization not found for connector instance.")
  }

  const memberRows = await db
    .select()
    .from(MemberTable)
    .where(and(
      eq(MemberTable.organizationId, input.connectorInstance.organizationId),
      eq(MemberTable.id, input.connectorInstance.createdByOrgMembershipId),
      isNull(MemberTable.removedAt),
    ))
    .limit(1)
  const member = memberRows[0] as MemberRow | undefined
  if (!member) {
    throw new PluginArchRouteFailure(404, "member_not_found", "Connector creator member not found.")
  }

  if (!member.userId) {
    throw new PluginArchRouteFailure(404, "member_not_joined", "Connector creator member has not joined the organization.")
  }

  return {
    automation: true,
    memberTeams: [],
    session: null,
    organizationContext: {
      currentMember: {
        createdAt: member.createdAt,
        id: member.id,
        isOwner: roleIncludesOwner(member.role),
        joinedAt: member.joinedAt,
        role: member.role,
        userId: member.userId,
      },
      invitations: [],
      members: [],
      organization: {

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Have the invited user accept the organization invitation so the membership gets a userId.
  2. Re-assign the connector instance's createdByOrgMembershipId to a member whose userId is set.
  3. If the pending membership is abandoned, remove it and re-create the connector under an active member.

Example fix

// before
const member = await getMember(membershipId)
await resolveConnectorActor(member) // member.userId is null -> member_not_joined
// after
if (!member.userId) {
  const joined = await db.select().from(MemberTable).where(and(eq(MemberTable.organizationId, orgId), isNotNull(MemberTable.userId), isNull(MemberTable.removedAt))).limit(1)
  await reassignConnectorCreator(connectorInstanceId, joined[0].id)
}
await resolveConnectorActor(joined[0])
Defensive patterns

Strategy: validation

Validate before calling

const member = await getMember(instance.createdByOrgMembershipId)
if (member && !member.userId) throw new Error('Creator has a pending invitation; complete signup or reassign the connector')

Type guard

function hasJoined(row: MemberRow): row is MemberRow & { userId: string } { return typeof row.userId === 'string' && row.userId.length > 0 }

Try / catch

try {
  await resolveConnectorActor({ connectorInstanceId })
} catch (e) {
  if (e instanceof PluginArchRouteFailure && e.code === 'member_not_joined') {
    await reassignConnectorCreator(connectorInstanceId, await firstJoinedMember(orgId))
  } else throw e
}

Prevention

When it happens

Trigger: Resolving a connector instance created by an invited-but-not-yet-accepted membership; a membership row provisioned programmatically (e.g. by SCIM or a script) that never got a userId attached.

Common situations: Admin invites a teammate, the teammate creates the connector in a pending state, invite is never accepted; automated provisioning creates membership rows before users complete onboarding.

Related errors


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