{"record":{"id":"bc7785fde9b48331","repo":"different-ai/openwork","slug":"member-not-joined","errorCode":"member_not_joined","errorMessage":"Connector creator member has not joined the organization.","messagePattern":"Connector creator member has not joined the organization\\.","errorType":"http","errorClass":"PluginArchRouteFailure","httpStatus":404,"severity":"error","filePath":"ee/apps/den-api/src/routes/org/plugin-system/store.ts","lineNumber":5875,"sourceCode":"    throw new PluginArchRouteFailure(404, \"organization_not_found\", \"Organization not found for connector instance.\")\n  }\n\n  const memberRows = await db\n    .select()\n    .from(MemberTable)\n    .where(and(\n      eq(MemberTable.organizationId, input.connectorInstance.organizationId),\n      eq(MemberTable.id, input.connectorInstance.createdByOrgMembershipId),\n      isNull(MemberTable.removedAt),\n    ))\n    .limit(1)\n  const member = memberRows[0] as MemberRow | undefined\n  if (!member) {\n    throw new PluginArchRouteFailure(404, \"member_not_found\", \"Connector creator member not found.\")\n  }\n\n  if (!member.userId) {\n    throw new PluginArchRouteFailure(404, \"member_not_joined\", \"Connector creator member has not joined the organization.\")\n  }\n\n  return {\n    automation: true,\n    memberTeams: [],\n    session: null,\n    organizationContext: {\n      currentMember: {\n        createdAt: member.createdAt,\n        id: member.id,\n        isOwner: roleIncludesOwner(member.role),\n        joinedAt: member.joinedAt,\n        role: member.role,\n        userId: member.userId,\n      },\n      invitations: [],\n      members: [],\n      organization: {","sourceCodeStart":5857,"sourceCodeEnd":5893,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/ee/apps/den-api/src/routes/org/plugin-system/store.ts#L5857-L5893","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Have the invited user accept the organization invitation so the membership gets a userId.","Re-assign the connector instance's createdByOrgMembershipId to a member whose userId is set.","If the pending membership is abandoned, remove it and re-create the connector under an active member."],"exampleFix":"// before\nconst member = await getMember(membershipId)\nawait resolveConnectorActor(member) // member.userId is null -> member_not_joined\n// after\nif (!member.userId) {\n  const joined = await db.select().from(MemberTable).where(and(eq(MemberTable.organizationId, orgId), isNotNull(MemberTable.userId), isNull(MemberTable.removedAt))).limit(1)\n  await reassignConnectorCreator(connectorInstanceId, joined[0].id)\n}\nawait resolveConnectorActor(joined[0])","handlingStrategy":"validation","validationCode":"const member = await getMember(instance.createdByOrgMembershipId)\nif (member && !member.userId) throw new Error('Creator has a pending invitation; complete signup or reassign the connector')","typeGuard":"function hasJoined(row: MemberRow): row is MemberRow & { userId: string } { return typeof row.userId === 'string' && row.userId.length > 0 }","tryCatchPattern":"try {\n  await resolveConnectorActor({ connectorInstanceId })\n} catch (e) {\n  if (e instanceof PluginArchRouteFailure && e.code === 'member_not_joined') {\n    await reassignConnectorCreator(connectorInstanceId, await firstJoinedMember(orgId))\n  } else throw e\n}","preventionTips":["Track invitation acceptance and block connector creation for unjoined members","Re-assign automations when invitations lapse","Alert on membership rows without userId older than a threshold"],"tags":["membership","onboarding","connector"],"backgroundTag":"unaccepted-invitation-membership","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}