we-promise/sure · warning · BrexItem::AccountFlow::AccountAlreadyLinkedError
BrexItem::AccountFlow::AccountAlreadyLinkedError
Error message
BrexItem::AccountFlow::AccountAlreadyLinkedError
What it means
Raised by BrexItem::AccountFlow#link_existing_account! as AccountAlreadyLinkedError when the freshly upserted BrexAccount record already has an account_provider — i.e. that Brex account is already linked to a Maybe account. The raise happens inside ActiveRecord::Base.transaction, so the new AccountProvider creation rolls back and no partial link survives.
Source
Thrown at app/models/brex_item/account_flow.rb:215
LinkAccountsResult.new(
created_accounts: created_accounts,
already_linked_names: already_linked_names,
invalid_account_ids: invalid_account_ids
)
end
def link_existing_account!(account:, brex_account_id:)
account_data = indexed_accounts[brex_account_id.to_s]
raise AccountNotFoundError unless account_data
account_name = BrexAccount.name_for(account_data)
raise InvalidAccountNameError if account_name.blank?
brex_account = nil
ActiveRecord::Base.transaction do
brex_account = upsert_brex_account!(brex_account_id, account_data)
raise AccountAlreadyLinkedError if brex_account.account_provider.present?
AccountProvider.create!(account: account, provider: brex_account)
end
brex_item.sync_later
brex_account
end
private
def selection_error_payload
if brex_item_id.present?
return {
success: false,
error: "select_connection",
error_message: I18n.t("brex_items.select_accounts.select_connection"),
has_accounts: nilView on GitHub (pinned to e69894adb9)
Solutions
- Drive the picker from select_existing_account_result (built on unlinked_available_accounts), which already filters linked accounts, so the option never appears.
- Rescue AccountAlreadyLinkedError and show the 'provider account already linked' alert — link_existing_account_result already maps this if you use the public wrapper.
- Unlink the existing account first (Unlinking concern) if you genuinely want to re-point it.
- Make the submit action idempotent (disable after click, POST-once tokens) to kill double-submit races.
Example fix
# before flow.link_existing_account!(account: acct, brex_account_id: id) # after — use the result wrapper that handles all link failures result = flow.link_existing_account_result(account: acct, brex_account_id: id) redirect_to accounts_path, result.flash_type => result.message # or, calling the bang method directly: begin flow.link_existing_account!(account: acct, brex_account_id: id) rescue BrexItem::AccountFlow::AccountAlreadyLinkedError redirect_to accounts_path, alert: "That Brex account is already linked." end
Defensive patterns
Strategy: validation
Validate before calling
# Build pickers only from accounts not yet linked result = flow.select_existing_account_result(accountable_type: "CreditCard") # filters via unlinked_available_accounts return if result.available_accounts.empty?
Try / catch
begin flow.link_existing_account!(account: account, brex_account_id: id) rescue BrexItem::AccountFlow::AccountAlreadyLinkedError redirect_to accounts_path, alert: "That Brex account is already linked to another account." end
Prevention
- Render linkable accounts from select_existing_account_result (backed by unlinked_available_accounts) so linked ones never appear.
- Make submit buttons idempotent (disable-on-click, one-time tokens) to prevent double-submit races.
- Unlink the existing account first when intentionally re-pointing a Brex account to a new Maybe account.
When it happens
Trigger: link_existing_account! runs after the same Brex account was already linked elsewhere: a stale picker showed it as available (the unfiltered accounts list includes linked ones), two tabs/users race to link it, or a retry resubmits after a first successful link (app/models/brex_item/account_flow.rb:213-215).
Common situations: Double-clicked submit buttons; back-button resubmission; UI list built from all provider accounts instead of unlinked_available_accounts; one Brex account intentionally or accidentally shared between two Maybe accounts.
Related errors
- BrexItem::AccountFlow::AccountNotFoundError
- BrexItem::AccountFlow::InvalidAccountNameError
- BrexItem::AccountFlow::NoApiTokenError
- bad_request
- unauthorized
AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21).
Data as JSON: /api/errors/f0e54680adf9a1f9.
Report an issue: GitHub.