plandex-ai/plandex · error

error creating org user: %v

Error message

error creating org user: %v

What it means

AcceptInvite runs inside a WithTx transaction: after marking the invite accepted, it calls CreateOrgUser to add the invitee to the org. If CreateOrgUser fails (e.g. role or org rows missing, DB constraint, duplicate org_users row), the error is wrapped as "error creating org user: %v" and the whole transaction rolls back, so the invite stays unaccepted.

Source

Thrown at app/server/db/invite_helpers.go:129

		return fmt.Errorf("error deleting invite: %v", err)
	}

	return nil
}

func AcceptInvite(ctx context.Context, invite *Invite, inviteeId string) error {
	err := WithTx(ctx, "accept invite", func(tx *sqlx.Tx) error {

		_, err := tx.Exec(`UPDATE invites SET accepted_at = NOW(), invitee_id = $1 WHERE id = $2`, inviteeId, invite.Id)
		if err != nil {
			return fmt.Errorf("error accepting invite: %v", err)
		}

		// create org user
		err = CreateOrgUser(invite.OrgId, inviteeId, invite.OrgRoleId, tx)

		if err != nil {
			return fmt.Errorf("error creating org user: %v", err)
		}

		return nil
	})

	if err != nil {
		return fmt.Errorf("error accepting invite: %v", err)
	}

	invite.InviteeId = &inviteeId

	return nil
}

View on GitHub (pinned to e2d772072e)

Solutions

  1. Check the wrapped inner error for a uniqueness/duplicate-key violation — the user is likely already a member; treat as success or delete the org_users row and retry.
  2. Verify invite.OrgId and invite.OrgRoleId still exist in orgs/org_roles before accepting; refresh or re-issue the invite if not.
  3. Retry AcceptInvite — the transaction rolled back, so the invite is still pending and a transient DB error can simply be retried.
  4. Inspect CreateOrgUser directly with the given orgId, userId, roleId to reproduce the underlying insert error.

Example fix

// before
err = AcceptInvite(ctx, invite, userId)
if err != nil { return err }
// after
err = AcceptInvite(ctx, invite, userId)
if err != nil {
    if strings.Contains(err.Error(), "duplicate key") {
        return nil // user already a member of the org
    }
    return err
}
Defensive patterns

Strategy: try-catch

Validate before calling

// before calling AcceptInvite
var orgExists, roleExists bool
if err := db.Conn.Get(&orgExists, "SELECT EXISTS(SELECT 1 FROM orgs WHERE id=$1)", invite.OrgId); err != nil || !orgExists {
    return fmt.Errorf("invite org %s no longer exists", invite.OrgId)
}
if err := db.Conn.Get(&roleExists, "SELECT EXISTS(SELECT 1 FROM org_roles WHERE id=$1)", invite.OrgRoleId); err != nil || !roleExists {
    return fmt.Errorf("invite role %s no longer exists", invite.OrgRoleId)
}

Type guard

func inviteValid(inv *db.Invite) bool {
    return inv != nil && inv.Id != "" && inv.OrgId != "" && inv.OrgRoleId != ""
}

Try / catch

err := AcceptInvite(ctx, invite, userId)
if err != nil {
    if strings.Contains(err.Error(), "error creating org user") && strings.Contains(err.Error(), "duplicate key") {
        return nil // already a member
    }
    return fmt.Errorf("accept invite failed: %w", err)
}

Prevention

When it happens

Trigger: Calling AcceptInvite when invite.OrgId or invite.OrgRoleId references a deleted org/org_role, when the user already has an org_users row for that org (unique constraint), or when the org_users insert hits any DB error.

Common situations: Accepting a stale invite after the org was deleted; accepting the same invite twice concurrently so both transactions race to insert the org user; an org_role id captured on the invite no longer exists after role cleanup.

Related errors


AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05). Data as JSON: /api/errors/c72a033b97e8d8de. Report an issue: GitHub.